ФУНКЦИИ - МЕЖПРОЕКТНАЯ КОММУНИКАЦИЯ: лимиты Voice Tasker до OpenAI pipeline
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
|
||||
import calendar
|
||||
import json
|
||||
import math
|
||||
import re
|
||||
import uuid
|
||||
from datetime import date, timedelta
|
||||
@@ -60,6 +61,11 @@ VOICE_TASK_INTENTS = {"create_task", "update_task", "delete_task", "unknown"}
|
||||
VOICE_TASK_PRIORITIES = {"none", "low", "medium", "high", "urgent"}
|
||||
VOICE_TASK_MEMORY_LIMIT = 5
|
||||
VOICE_TASK_CONTEXT_LIMIT = 100
|
||||
VOICE_TASK_RATE_LIMIT_WINDOW_SECONDS = 60 * 60
|
||||
VOICE_TASK_RATE_LIMIT_ERROR_CODES = {
|
||||
"voice_task_user_hourly_limit_exceeded",
|
||||
"voice_task_workspace_hourly_limit_exceeded",
|
||||
}
|
||||
VOICE_TASK_PROJECT_MATCH_THRESHOLD = 0.8
|
||||
VOICE_TASK_ASSIGNEE_MATCH_THRESHOLD = 0.8
|
||||
VOICE_TASK_STATE_MATCH_THRESHOLD = 0.8
|
||||
@@ -308,6 +314,111 @@ def get_voice_task_preflight(workspace, user, project_id=None):
|
||||
return response
|
||||
|
||||
|
||||
def get_voice_task_rate_limit_state(workspace, user, ai_settings, now=None):
|
||||
now = now or timezone.now()
|
||||
window_start = now - timedelta(seconds=VOICE_TASK_RATE_LIMIT_WINDOW_SECONDS)
|
||||
sessions = VoiceTaskSession.objects.filter(
|
||||
workspace=workspace,
|
||||
created_at__gte=window_start,
|
||||
created_at__lte=now,
|
||||
).exclude(error_code__in=VOICE_TASK_RATE_LIMIT_ERROR_CODES)
|
||||
user_sessions = sessions.filter(user=user)
|
||||
|
||||
user_used = user_sessions.count()
|
||||
workspace_used = sessions.count()
|
||||
user_limit = max(int(ai_settings.per_user_hourly_limit or 0), 0)
|
||||
workspace_limit = max(int(ai_settings.workspace_hourly_limit or 0), 0)
|
||||
|
||||
return {
|
||||
"window_start": window_start,
|
||||
"window_seconds": VOICE_TASK_RATE_LIMIT_WINDOW_SECONDS,
|
||||
"user": {
|
||||
"used": user_used,
|
||||
"limit": user_limit,
|
||||
"exceeded": bool(user_limit and user_used >= user_limit),
|
||||
},
|
||||
"workspace": {
|
||||
"used": workspace_used,
|
||||
"limit": workspace_limit,
|
||||
"exceeded": bool(workspace_limit and workspace_used >= workspace_limit),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def get_voice_task_rate_limit_retry_after(workspace, user, scope, now=None):
|
||||
now = now or timezone.now()
|
||||
window_start = now - timedelta(seconds=VOICE_TASK_RATE_LIMIT_WINDOW_SECONDS)
|
||||
sessions = VoiceTaskSession.objects.filter(
|
||||
workspace=workspace,
|
||||
created_at__gte=window_start,
|
||||
created_at__lte=now,
|
||||
).exclude(error_code__in=VOICE_TASK_RATE_LIMIT_ERROR_CODES)
|
||||
|
||||
if scope == "user":
|
||||
sessions = sessions.filter(user=user)
|
||||
|
||||
oldest_session_at = sessions.order_by("created_at").values_list("created_at", flat=True).first()
|
||||
reset_at = (oldest_session_at or now) + timedelta(seconds=VOICE_TASK_RATE_LIMIT_WINDOW_SECONDS)
|
||||
retry_after = max(1, math.ceil((reset_at - now).total_seconds()))
|
||||
|
||||
return retry_after, reset_at
|
||||
|
||||
|
||||
def get_voice_task_rate_limit_error(workspace, user, ai_settings, now=None):
|
||||
now = now or timezone.now()
|
||||
state = get_voice_task_rate_limit_state(workspace, user, ai_settings, now=now)
|
||||
|
||||
if state["user"]["exceeded"]:
|
||||
retry_after, reset_at = get_voice_task_rate_limit_retry_after(workspace, user, "user", now=now)
|
||||
return {
|
||||
"code": "voice_task_user_hourly_limit_exceeded",
|
||||
"message": "Voice Tasker user hourly limit exceeded.",
|
||||
"scope": "user",
|
||||
"limit": state["user"]["limit"],
|
||||
"used": state["user"]["used"],
|
||||
"window_seconds": state["window_seconds"],
|
||||
"retry_after": retry_after,
|
||||
"reset_at": reset_at,
|
||||
}
|
||||
|
||||
if state["workspace"]["exceeded"]:
|
||||
retry_after, reset_at = get_voice_task_rate_limit_retry_after(workspace, user, "workspace", now=now)
|
||||
return {
|
||||
"code": "voice_task_workspace_hourly_limit_exceeded",
|
||||
"message": "Voice Tasker workspace hourly limit exceeded.",
|
||||
"scope": "workspace",
|
||||
"limit": state["workspace"]["limit"],
|
||||
"used": state["workspace"]["used"],
|
||||
"window_seconds": state["window_seconds"],
|
||||
"retry_after": retry_after,
|
||||
"reset_at": reset_at,
|
||||
}
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def create_voice_task_rate_limit_session(
|
||||
workspace,
|
||||
user,
|
||||
audio,
|
||||
audio_content_type,
|
||||
duration_seconds,
|
||||
client_context,
|
||||
rate_limit_error,
|
||||
):
|
||||
return VoiceTaskSession.objects.create(
|
||||
workspace=workspace,
|
||||
user=user,
|
||||
status=VoiceTaskSession.Status.FAILED,
|
||||
audio_duration_seconds=duration_seconds,
|
||||
audio_content_type=audio_content_type,
|
||||
audio_size=getattr(audio, "size", None),
|
||||
client_context=client_context,
|
||||
error_code=rate_limit_error["code"],
|
||||
error_message=rate_limit_error["message"],
|
||||
)
|
||||
|
||||
|
||||
class VoiceTaskerPipelineError(Exception):
|
||||
def __init__(self, code, message, response_status=status.HTTP_400_BAD_REQUEST):
|
||||
self.code = code
|
||||
@@ -2440,6 +2551,40 @@ class VoiceTaskParseEndpoint(BaseAPIView):
|
||||
if not isinstance(client_context, dict):
|
||||
client_context = {}
|
||||
|
||||
ai_settings = WorkspaceAISettings.objects.filter(workspace=workspace).first()
|
||||
if not ai_settings:
|
||||
return Response(
|
||||
{"ok": False, "code": "not_configured", "error": "Voice Tasker is not configured for this workspace."},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
rate_limit_error = get_voice_task_rate_limit_error(workspace, request.user, ai_settings)
|
||||
if rate_limit_error:
|
||||
voice_session = create_voice_task_rate_limit_session(
|
||||
workspace=workspace,
|
||||
user=request.user,
|
||||
audio=audio,
|
||||
audio_content_type=audio_content_type,
|
||||
duration_seconds=duration_seconds,
|
||||
client_context=client_context,
|
||||
rate_limit_error=rate_limit_error,
|
||||
)
|
||||
return Response(
|
||||
{
|
||||
"ok": False,
|
||||
"voice_session_id": str(voice_session.id),
|
||||
"code": rate_limit_error["code"],
|
||||
"error": rate_limit_error["message"],
|
||||
"limit_scope": rate_limit_error["scope"],
|
||||
"limit": rate_limit_error["limit"],
|
||||
"used": rate_limit_error["used"],
|
||||
"window_seconds": rate_limit_error["window_seconds"],
|
||||
"retry_after": rate_limit_error["retry_after"],
|
||||
"reset_at": rate_limit_error["reset_at"].isoformat(),
|
||||
},
|
||||
status=status.HTTP_429_TOO_MANY_REQUESTS,
|
||||
)
|
||||
|
||||
voice_session = VoiceTaskSession.objects.create(
|
||||
workspace=workspace,
|
||||
user=request.user,
|
||||
|
||||
Reference in New Issue
Block a user