refactor(proxy): rename usage_endpoints to dashboard_ai and the Ask AI route to /dashboard/ai/chat

The Ask AI feature lived under usage_endpoints and served /usage/ai/chat, which reads like the home for all usage/spend endpoints when it is really one AI chat feature that happens to sit on the Usage dashboard, and it collided in basename with the unrelated guardrails/usage_endpoints module. This renames the package to dashboard_ai and the route to /dashboard/ai/chat so it is positioned as the dashboard's AI endpoint rather than a usage-only one, ahead of extending it to more of the dashboard

The chat-feature symbols move with it (usage_ai_chat to dashboard_ai_chat, UsageAIChatRequest to DashboardAIChatRequest, stream_usage_ai_chat to stream_dashboard_ai_chat). The scoped data-access layer keeps its usage-oriented names (ScopedUsageDataProvider, AdminScope, UserScope) because it still reads usage and spend data. The lazy-feature registration, the dashboard client URL, and the regenerated OpenAPI snapshot and dashboard types are updated to match

The moved modules drop Optional[...] for the modern X | None form so they stay within the ruff strict budget, and ruff-strict-budget.json is ratcheted down by the violations this replacement cleared
This commit is contained in:
ryan-crabbe-berri 2026-07-12 21:23:33 -07:00
parent 5ce28fb8e2
commit 4089198b06
14 changed files with 287 additions and 284 deletions

View file

@ -229,9 +229,9 @@ LAZY_FEATURES: Tuple[LazyFeature, ...] = (
path_prefixes=("/vantage",),
),
LazyFeature(
name="usage_ai",
module_path="litellm.proxy.management_endpoints.usage_endpoints",
path_prefixes=("/usage/ai",),
name="dashboard_ai",
module_path="litellm.proxy.management_endpoints.dashboard_ai",
path_prefixes=("/dashboard/ai",),
),
LazyFeature(
name="prompts",

View file

@ -6132,6 +6132,156 @@
}
}
},
"dashboard_ai": {
"components": {
"schemas": {
"ChatMessage": {
"properties": {
"content": {
"title": "Content",
"type": "string"
},
"role": {
"enum": [
"user",
"assistant"
],
"title": "Role",
"type": "string"
}
},
"required": [
"role",
"content"
],
"title": "ChatMessage",
"type": "object"
},
"DashboardAIChatRequest": {
"properties": {
"messages": {
"description": "Chat messages (user/assistant history)",
"items": {
"$ref": "#/components/schemas/ChatMessage"
},
"title": "Messages",
"type": "array"
},
"model": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Model group to use for AI chat",
"title": "Model"
}
},
"required": [
"messages"
],
"title": "DashboardAIChatRequest",
"type": "object"
},
"HTTPValidationError": {
"properties": {
"detail": {
"items": {
"$ref": "#/components/schemas/ValidationError"
},
"title": "Detail",
"type": "array"
}
},
"title": "HTTPValidationError",
"type": "object"
},
"ValidationError": {
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "integer"
}
]
},
"title": "Location",
"type": "array"
},
"msg": {
"title": "Message",
"type": "string"
},
"type": {
"title": "Error Type",
"type": "string"
}
},
"required": [
"loc",
"msg",
"type"
],
"title": "ValidationError",
"type": "object"
}
}
},
"paths": {
"/dashboard/ai/chat": {
"post": {
"description": "AI chat about usage data. Streams SSE events with the AI response.\n\nThe agent queries aggregated daily activity data through a provider scoped\nto the caller: admins get a global view, non-admins are restricted to their\nown ``user_id``.",
"operationId": "dashboard_ai_chat_dashboard_ai_chat_post",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/DashboardAIChatRequest"
}
}
},
"required": true
},
"responses": {
"200": {
"content": {
"application/json": {
"schema": {}
}
},
"description": "Successful Response"
},
"422": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
"description": "Validation Error"
}
},
"security": [
{
"APIKeyHeader": []
}
],
"summary": "Dashboard Ai Chat",
"tags": [
"usage_ai"
]
}
}
}
},
"evals": {
"components": {
"schemas": {
@ -27576,156 +27726,6 @@
}
}
},
"usage_ai": {
"components": {
"schemas": {
"ChatMessage": {
"properties": {
"content": {
"title": "Content",
"type": "string"
},
"role": {
"enum": [
"user",
"assistant"
],
"title": "Role",
"type": "string"
}
},
"required": [
"role",
"content"
],
"title": "ChatMessage",
"type": "object"
},
"HTTPValidationError": {
"properties": {
"detail": {
"items": {
"$ref": "#/components/schemas/ValidationError"
},
"title": "Detail",
"type": "array"
}
},
"title": "HTTPValidationError",
"type": "object"
},
"UsageAIChatRequest": {
"properties": {
"messages": {
"description": "Chat messages (user/assistant history)",
"items": {
"$ref": "#/components/schemas/ChatMessage"
},
"title": "Messages",
"type": "array"
},
"model": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Model group to use for AI chat",
"title": "Model"
}
},
"required": [
"messages"
],
"title": "UsageAIChatRequest",
"type": "object"
},
"ValidationError": {
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "integer"
}
]
},
"title": "Location",
"type": "array"
},
"msg": {
"title": "Message",
"type": "string"
},
"type": {
"title": "Error Type",
"type": "string"
}
},
"required": [
"loc",
"msg",
"type"
],
"title": "ValidationError",
"type": "object"
}
}
},
"paths": {
"/usage/ai/chat": {
"post": {
"description": "AI chat about usage data. Streams SSE events with the AI response.\n\nThe agent queries aggregated daily activity data through a provider scoped\nto the caller: admins get a global view, non-admins are restricted to their\nown ``user_id``.",
"operationId": "usage_ai_chat_usage_ai_chat_post",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UsageAIChatRequest"
}
}
},
"required": true
},
"responses": {
"200": {
"content": {
"application/json": {
"schema": {}
}
},
"description": "Successful Response"
},
"422": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
"description": "Validation Error"
}
},
"security": [
{
"APIKeyHeader": []
}
],
"summary": "Usage Ai Chat",
"tags": [
"usage_ai"
]
}
}
}
},
"vantage": {
"components": {
"schemas": {

View file

@ -0,0 +1,9 @@
"""
Dashboard AI endpoints package.
Re-exports the router from endpoints module.
"""
from litellm.proxy.management_endpoints.dashboard_ai.endpoints import ( # noqa: F401
router,
)

View file

@ -10,13 +10,18 @@ rate-limited, and guardrailed like any other proxy request.
import json
from dataclasses import dataclass
from datetime import date
from typing import Any, AsyncIterator, Dict, List, Literal, Optional, Set, Union, cast
from typing import Any, AsyncIterator, Dict, List, Literal, Set, Union, cast
from pydantic import BaseModel, TypeAdapter
from typing_extensions import TypedDict, assert_never
import litellm
from litellm._logging import verbose_proxy_logger
from litellm.proxy.management_endpoints.dashboard_ai.scoped_data import (
ScopedUsageDataProvider,
summarise_entity_data,
summarise_usage_data,
)
from litellm.router import Router
from litellm.types.llms.openai import AllMessageValues
from litellm.types.utils import (
@ -25,11 +30,6 @@ from litellm.types.utils import (
Message,
ModelResponse,
)
from litellm.proxy.management_endpoints.usage_endpoints.scoped_data import (
ScopedUsageDataProvider,
summarise_entity_data,
summarise_usage_data,
)
USAGE_AI_TEMPERATURE = 0.2
MAX_CHAT_MESSAGES = 20
@ -127,7 +127,7 @@ def _require_router() -> Router:
return llm_router
def _assembled_message(chunks: List[object]) -> Optional[Message]:
def _assembled_message(chunks: List[object]) -> Message | None:
"""Reassemble streamed chunks into a single message (content + tool_calls)."""
built = litellm.stream_chunk_builder(chunks)
if not isinstance(built, ModelResponse) or not built.choices:
@ -136,7 +136,7 @@ def _assembled_message(chunks: List[object]) -> Optional[Message]:
return choice.message if isinstance(choice, Choices) else None # pyright: ignore[reportUnnecessaryIsInstance] # choices[0] can be StreamingChoices at runtime
def resolve_model(requested: Optional[str]) -> Union[str, ModelNotConfigured]:
def resolve_model(requested: str | None) -> Union[str, ModelNotConfigured]:
"""Resolve the model group to use: explicit request wins, then the
configured ``usage_ai_model`` setting, else an actionable error value."""
explicit = (requested or "").strip()
@ -145,7 +145,7 @@ def resolve_model(requested: Optional[str]) -> Union[str, ModelNotConfigured]:
from litellm.proxy.proxy_server import general_settings
configured = TypeAdapter(Optional[str]).validate_python(general_settings.get(USAGE_AI_MODEL_SETTING))
configured = TypeAdapter(str | None).validate_python(general_settings.get(USAGE_AI_MODEL_SETTING))
stripped = (configured or "").strip()
return stripped or ModelNotConfigured()
@ -235,19 +235,19 @@ _TOOL_LABELS = {
class _UsageArgs(BaseModel):
start_date: str
end_date: str
user_id: Optional[str] = None
user_id: str | None = None
class _TeamArgs(BaseModel):
start_date: str
end_date: str
team_ids: Optional[str] = None
team_ids: str | None = None
class _TagArgs(BaseModel):
start_date: str
end_date: str
tags: Optional[str] = None
tags: str | None = None
async def _dispatch_tool(name: str, raw_args: Dict[str, Any], provider: ScopedUsageDataProvider) -> str:
@ -341,10 +341,10 @@ async def _run_tool_call(
convo.append({"role": "tool", "tool_call_id": tc.id, "content": result})
async def stream_usage_ai_chat(
async def stream_dashboard_ai_chat(
provider: ScopedUsageDataProvider,
messages: List[Dict[str, str]],
model: Optional[str] = None,
model: str | None = None,
) -> AsyncIterator[str]:
"""Stream SSE events: status -> tool_call -> chunk -> done (or a single error)."""
resolved = resolve_model(model)

View file

@ -1,10 +1,10 @@
"""
USAGE AI CHAT ENDPOINT
DASHBOARD AI CHAT ENDPOINT
/usage/ai/chat - Stream AI chat responses about usage data
/dashboard/ai/chat - Stream AI chat responses about usage data
"""
from typing import List, Literal, Optional
from typing import List, Literal
from fastapi import APIRouter, Depends, Request
from fastapi.responses import StreamingResponse
@ -21,18 +21,18 @@ class ChatMessage(BaseModel):
content: str
class UsageAIChatRequest(BaseModel):
class DashboardAIChatRequest(BaseModel):
messages: List[ChatMessage] = Field(..., description="Chat messages (user/assistant history)")
model: Optional[str] = Field(default=None, description="Model group to use for AI chat")
model: str | None = Field(default=None, description="Model group to use for AI chat")
@router.post(
"/usage/ai/chat",
"/dashboard/ai/chat",
tags=["Budget & Spend Tracking"],
dependencies=[Depends(user_api_key_auth)],
)
async def usage_ai_chat(
data: UsageAIChatRequest,
async def dashboard_ai_chat(
data: DashboardAIChatRequest,
request: Request,
user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth),
):
@ -47,10 +47,10 @@ async def usage_ai_chat(
from litellm.proxy.management_endpoints.common_utils import (
require_caller_user_id_for_non_admin,
)
from litellm.proxy.management_endpoints.usage_endpoints.agent import (
stream_usage_ai_chat,
from litellm.proxy.management_endpoints.dashboard_ai.agent import (
stream_dashboard_ai_chat,
)
from litellm.proxy.management_endpoints.usage_endpoints.scoped_data import (
from litellm.proxy.management_endpoints.dashboard_ai.scoped_data import (
AdminScope,
ScopedUsageDataProvider,
UserScope,
@ -66,7 +66,7 @@ async def usage_ai_chat(
messages = [{"role": m.role, "content": m.content} for m in data.messages]
return StreamingResponse(
stream_usage_ai_chat(provider=provider, messages=messages, model=data.model),
stream_dashboard_ai_chat(provider=provider, messages=messages, model=data.model),
media_type="text/event-stream",
headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"},
)

View file

@ -42,7 +42,7 @@ class AdminScope:
"""Global view. ``caller_user_id`` is the admin's own id (may be None) and
is not used to filter; admins may optionally pass an explicit user filter."""
caller_user_id: Optional[str]
caller_user_id: str | None
@dataclass(frozen=True, slots=True)
@ -55,7 +55,7 @@ class UserScope:
AiChatScope = Union[AdminScope, UserScope]
def _parse_csv(raw: Optional[str]) -> Optional[List[str]]:
def _parse_csv(raw: str | None) -> List[str] | None:
if not raw:
return None
return [t.strip() for t in raw.split(",") if t.strip()]
@ -73,7 +73,7 @@ class ScopedUsageDataProvider:
return isinstance(self._scope, AdminScope)
async def usage(
self, start_date: str, end_date: str, user_id_filter: Optional[str]
self, start_date: str, end_date: str, user_id_filter: str | None
) -> SpendAnalyticsPaginatedResponse:
scope = self._scope
effective_user_id = scope.user_id if isinstance(scope, UserScope) else user_id_filter
@ -93,13 +93,13 @@ class ScopedUsageDataProvider:
api_key=None,
)
async def team(self, start_date: str, end_date: str, team_ids: Optional[str]) -> SpendAnalyticsPaginatedResponse:
async def team(self, start_date: str, end_date: str, team_ids: str | None) -> SpendAnalyticsPaginatedResponse:
self._require_admin("team usage")
return await self._paginated(
TABLE_DAILY_TEAM_SPEND, ENTITY_FIELD_TEAM, _parse_csv(team_ids), start_date, end_date
)
async def tag(self, start_date: str, end_date: str, tags: Optional[str]) -> SpendAnalyticsPaginatedResponse:
async def tag(self, start_date: str, end_date: str, tags: str | None) -> SpendAnalyticsPaginatedResponse:
self._require_admin("tag usage")
return await self._paginated(TABLE_DAILY_TAG_SPEND, ENTITY_FIELD_TAG, _parse_csv(tags), start_date, end_date)
@ -111,7 +111,7 @@ class ScopedUsageDataProvider:
self,
table_name: str,
entity_id_field: str,
entity_id: Optional[List[str]],
entity_id: List[str] | None,
start_date: str,
end_date: str,
) -> SpendAnalyticsPaginatedResponse:

View file

@ -1,9 +0,0 @@
"""
Usage endpoints package.
Re-exports the router from endpoints module.
"""
from litellm.proxy.management_endpoints.usage_endpoints.endpoints import ( # noqa: F401
router,
)

View file

@ -24,7 +24,7 @@
"limit": 130
},
"ANN401": {
"limit": 2075
"limit": 2073
},
"ASYNC230": {
"limit": 14
@ -324,7 +324,7 @@
"limit": 883
},
"UP006": {
"limit": 12792
"limit": 12780
},
"UP007": {
"limit": 2570
@ -363,6 +363,6 @@
"limit": 105
},
"UP045": {
"limit": 18462
"limit": 18450
}
}

View file

@ -20,17 +20,17 @@ from litellm.types.utils import (
ModelResponseStream,
StreamingChoices,
)
from litellm.proxy.management_endpoints.usage_endpoints import agent as agent_mod
from litellm.proxy.management_endpoints.usage_endpoints.agent import (
from litellm.proxy.management_endpoints.dashboard_ai import agent as agent_mod
from litellm.proxy.management_endpoints.dashboard_ai.agent import (
LLMCallError,
ModelNotConfigured,
RouterUnavailable,
_error_event,
resolve_model,
stream_usage_ai_chat,
stream_dashboard_ai_chat,
tools_for_role,
)
from litellm.proxy.management_endpoints.usage_endpoints.scoped_data import (
from litellm.proxy.management_endpoints.dashboard_ai.scoped_data import (
AdminScope,
ScopedUsageDataProvider,
UserScope,
@ -98,7 +98,7 @@ async def _collect(provider, messages, model, router):
):
mock_agg.return_value = _usage_response_mock()
events = []
async for raw in stream_usage_ai_chat(provider=provider, messages=messages, model=model):
async for raw in stream_dashboard_ai_chat(provider=provider, messages=messages, model=model):
events.append(json.loads(raw.replace("data: ", "").strip()))
return events, mock_agg
@ -184,7 +184,7 @@ class TestMultiRoundLoop:
mock_paginated.return_value = _usage_response_mock()
events = [
json.loads(raw.replace("data: ", "").strip())
async for raw in stream_usage_ai_chat(
async for raw in stream_dashboard_ai_chat(
provider=provider, messages=[{"role": "user", "content": "q"}], model="m"
)
]
@ -224,7 +224,7 @@ class TestErrorPaths:
):
events = [
json.loads(raw.replace("data: ", "").strip())
async for raw in stream_usage_ai_chat(
async for raw in stream_dashboard_ai_chat(
provider=provider, messages=[{"role": "user", "content": "q"}], model=None
)
]
@ -240,7 +240,7 @@ class TestErrorPaths:
with patch.object(agent_mod, "_require_router", side_effect=agent_mod._RouterUnavailableError()):
events = [
json.loads(raw.replace("data: ", "").strip())
async for raw in stream_usage_ai_chat(
async for raw in stream_dashboard_ai_chat(
provider=provider, messages=[{"role": "user", "content": "q"}], model="m"
)
]
@ -257,7 +257,7 @@ class TestErrorPaths:
with patch.object(agent_mod, "_require_router", return_value=broken_router):
events = [
json.loads(raw.replace("data: ", "").strip())
async for raw in stream_usage_ai_chat(
async for raw in stream_dashboard_ai_chat(
provider=provider, messages=[{"role": "user", "content": "q"}], model="m"
)
]

View file

@ -1,4 +1,4 @@
"""Endpoint-boundary tests for /usage/ai/chat.
"""Endpoint-boundary tests for /dashboard/ai/chat.
Security regression: a non-admin caller with user_id=None (a service-account
key) must be rejected at the endpoint before any scope/provider is built, so it
@ -11,10 +11,10 @@ import pytest
from fastapi import HTTPException
from litellm.proxy._types import LitellmUserRoles, UserAPIKeyAuth
from litellm.proxy.management_endpoints.usage_endpoints.endpoints import (
from litellm.proxy.management_endpoints.dashboard_ai.endpoints import (
ChatMessage,
UsageAIChatRequest,
usage_ai_chat,
DashboardAIChatRequest,
dashboard_ai_chat,
)
@ -22,10 +22,10 @@ class TestServiceAccountGuard:
@pytest.mark.asyncio
async def test_non_admin_with_user_id_none_is_rejected(self):
service_account_key = UserAPIKeyAuth(user_id=None, user_role=LitellmUserRoles.INTERNAL_USER)
body = UsageAIChatRequest(messages=[ChatMessage(role="user", content="hi")], model="m")
body = DashboardAIChatRequest(messages=[ChatMessage(role="user", content="hi")], model="m")
with pytest.raises(HTTPException) as exc_info:
await usage_ai_chat(data=body, request=MagicMock(), user_api_key_dict=service_account_key)
await dashboard_ai_chat(data=body, request=MagicMock(), user_api_key_dict=service_account_key)
assert exc_info.value.status_code == 403
assert "Service-account keys" in str(exc_info.value.detail)
@ -35,7 +35,7 @@ class TestScopeSelection:
@pytest.mark.asyncio
async def test_admin_caller_builds_admin_scope(self):
admin_key = UserAPIKeyAuth(user_id="admin-1", user_role=LitellmUserRoles.PROXY_ADMIN)
body = UsageAIChatRequest(messages=[ChatMessage(role="user", content="hi")], model="m")
body = DashboardAIChatRequest(messages=[ChatMessage(role="user", content="hi")], model="m")
captured = {}
@ -53,10 +53,10 @@ class TestScopeSelection:
try:
with pytest.MonkeyPatch.context() as mp:
mp.setattr(
"litellm.proxy.management_endpoints.usage_endpoints.agent.stream_usage_ai_chat",
"litellm.proxy.management_endpoints.dashboard_ai.agent.stream_dashboard_ai_chat",
_fake_stream,
)
response = await usage_ai_chat(data=body, request=MagicMock(), user_api_key_dict=admin_key)
response = await dashboard_ai_chat(data=body, request=MagicMock(), user_api_key_dict=admin_key)
# Drain the streaming body so the generator runs.
async for _ in response.body_iterator:
pass

View file

@ -9,7 +9,7 @@ from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from litellm.proxy.management_endpoints.usage_endpoints.scoped_data import (
from litellm.proxy.management_endpoints.dashboard_ai.scoped_data import (
AdminScope,
ScopedUsageDataProvider,
UserScope,

View file

@ -4187,7 +4187,7 @@ export const usageAiChatStream = async (
onToolCall?: (event: UsageAiToolCallEvent) => void,
signal?: AbortSignal,
) => {
const url = proxyBaseUrl ? `${proxyBaseUrl}/usage/ai/chat` : `/usage/ai/chat`;
const url = proxyBaseUrl ? `${proxyBaseUrl}/dashboard/ai/chat` : `/dashboard/ai/chat`;
const response = await fetch(url, {
method: "POST",

View file

@ -3078,6 +3078,30 @@ export interface paths {
patch?: never;
trace?: never;
};
"/dashboard/ai/chat": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
get?: never;
put?: never;
/**
* Dashboard Ai Chat
* @description AI chat about usage data. Streams SSE events with the AI response.
*
* The agent queries aggregated daily activity data through a provider scoped
* to the caller: admins get a global view, non-admins are restricted to their
* own ``user_id``.
*/
post: operations["dashboard_ai_chat_dashboard_ai_chat_post"];
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/debug/asyncio-tasks": {
parameters: {
query?: never;
@ -14355,27 +14379,6 @@ export interface paths {
patch?: never;
trace?: never;
};
"/usage/ai/chat": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
get?: never;
put?: never;
/**
* Usage Ai Chat
* @description AI chat about usage data. Streams SSE events with the AI response.
* The AI agent has access to tools that query aggregated daily activity data.
*/
post: operations["usage_ai_chat_usage_ai_chat_post"];
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/user/available_roles": {
parameters: {
query?: never;
@ -23254,6 +23257,19 @@ export interface components {
* DefaultInternalUserParams
* @description Default parameters to apply when a new user signs in via SSO or is created on the /user/new API endpoint
*/
/** DashboardAIChatRequest */
DashboardAIChatRequest: {
/**
* Messages
* @description Chat messages (user/assistant history)
*/
messages: components["schemas"]["ChatMessage"][];
/**
* Model
* @description Model group to use for AI chat
*/
model?: string | null;
};
DefaultInternalUserParams: {
/**
* Budget Duration
@ -32601,19 +32617,6 @@ export interface components {
/** User Role */
user_role?: ("proxy_admin" | "proxy_admin_viewer" | "internal_user" | "internal_user_viewer") | null;
};
/** UsageAIChatRequest */
UsageAIChatRequest: {
/**
* Messages
* @description Chat messages (user/assistant history)
*/
messages: components["schemas"]["ChatMessage"][];
/**
* Model
* @description Model group to use for AI chat
*/
model?: string | null;
};
/** UsageDetailResponse */
UsageDetailResponse: {
/** Avglatency */
@ -33682,6 +33685,39 @@ export interface components {
}
export type $defs = Record<string, never>;
export interface operations {
dashboard_ai_chat_dashboard_ai_chat_post: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody: {
content: {
"application/json": components["schemas"]["DashboardAIChatRequest"];
};
};
responses: {
/** @description Successful Response */
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": unknown;
};
};
/** @description Validation Error */
422: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["HTTPValidationError"];
};
};
};
};
home__get: {
parameters: {
query?: never;
@ -51294,39 +51330,6 @@ export interface operations {
};
};
};
usage_ai_chat_usage_ai_chat_post: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody: {
content: {
"application/json": components["schemas"]["UsageAIChatRequest"];
};
};
responses: {
/** @description Successful Response */
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": unknown;
};
};
/** @description Validation Error */
422: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["HTTPValidationError"];
};
};
};
};
ui_get_available_role_user_available_roles_get: {
parameters: {
query?: never;