From 6d92b13c22c7be0fe40518a4cf5af088f2f50a0a Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Fri, 19 Apr 2024 10:29:42 -0700 Subject: [PATCH 01/12] feat - log team_alias to langfuse --- litellm/proxy/_types.py | 1 + litellm/proxy/utils.py | 1 + 2 files changed, 2 insertions(+) diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index 6dbeed4753..b697b6e976 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -792,6 +792,7 @@ class LiteLLM_VerificationTokenView(LiteLLM_VerificationToken): """ team_spend: Optional[float] = None + team_alias: Optional[str] = None team_tpm_limit: Optional[int] = None team_rpm_limit: Optional[int] = None team_max_budget: Optional[float] = None diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index 954d2496ea..02e8a41668 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -1186,6 +1186,7 @@ class PrismaClient: t.rpm_limit AS team_rpm_limit, t.models AS team_models, t.blocked AS team_blocked, + t.team_alias AS team_alias, m.aliases as team_model_aliases FROM "LiteLLM_VerificationToken" AS v LEFT JOIN "LiteLLM_TeamTable" AS t ON v.team_id = t.team_id From 423121ff7dfccf1a2591d37c4ba0309d7962ac09 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Fri, 19 Apr 2024 10:52:54 -0700 Subject: [PATCH 02/12] feat - track team_alias is metadata for /chat, /embeddings --- litellm/proxy/proxy_server.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index c7415dea46..db85b7ba10 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -3361,6 +3361,9 @@ async def completion( data["metadata"]["user_api_key_team_id"] = getattr( user_api_key_dict, "team_id", None ) + data["metadata"]["user_api_key_team_alias"] = getattr( + user_api_key_dict, "team_alias", None + ) _headers = dict(request.headers) _headers.pop( "authorization", None @@ -3562,6 +3565,9 @@ async def chat_completion( data["metadata"]["user_api_key_team_id"] = getattr( user_api_key_dict, "team_id", None ) + data["metadata"]["user_api_key_team_alias"] = getattr( + user_api_key_dict, "team_alias", None + ) data["metadata"]["user_api_key_metadata"] = user_api_key_dict.metadata _headers = dict(request.headers) _headers.pop( @@ -3793,6 +3799,9 @@ async def embeddings( data["metadata"]["user_api_key_team_id"] = getattr( user_api_key_dict, "team_id", None ) + data["metadata"]["user_api_key_team_alias"] = getattr( + user_api_key_dict, "team_alias", None + ) data["metadata"]["endpoint"] = str(request.url) ### TEAM-SPECIFIC PARAMS ### @@ -3971,6 +3980,9 @@ async def image_generation( data["metadata"]["user_api_key_team_id"] = getattr( user_api_key_dict, "team_id", None ) + data["metadata"]["user_api_key_team_alias"] = getattr( + user_api_key_dict, "team_alias", None + ) data["metadata"]["endpoint"] = str(request.url) ### TEAM-SPECIFIC PARAMS ### @@ -4127,6 +4139,9 @@ async def audio_transcriptions( data["metadata"]["user_api_key_team_id"] = getattr( user_api_key_dict, "team_id", None ) + data["metadata"]["user_api_key_team_alias"] = getattr( + user_api_key_dict, "team_alias", None + ) data["metadata"]["endpoint"] = str(request.url) data["metadata"]["file_name"] = file.filename @@ -4302,6 +4317,9 @@ async def moderations( data["metadata"]["user_api_key_team_id"] = getattr( user_api_key_dict, "team_id", None ) + data["metadata"]["user_api_key_team_alias"] = getattr( + user_api_key_dict, "team_alias", None + ) data["metadata"]["endpoint"] = str(request.url) ### TEAM-SPECIFIC PARAMS ### From 3c6b6355c7ffaad28fe8aab3e39f8e380fd5266b Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Fri, 19 Apr 2024 13:01:52 -0700 Subject: [PATCH 03/12] fix(ollama_chat.py): accept api key as a param for ollama calls allows user to call hosted ollama endpoint using bearer token for auth --- litellm/__init__.py | 1 + litellm/llms/ollama_chat.py | 64 ++++++++++++++++++++------- litellm/main.py | 7 +++ litellm/proxy/_new_secret_config.yaml | 12 ++--- 4 files changed, 63 insertions(+), 21 deletions(-) diff --git a/litellm/__init__.py b/litellm/__init__.py index 5ef78dce4d..21f98e8b36 100644 --- a/litellm/__init__.py +++ b/litellm/__init__.py @@ -51,6 +51,7 @@ replicate_key: Optional[str] = None cohere_key: Optional[str] = None maritalk_key: Optional[str] = None ai21_key: Optional[str] = None +ollama_key: Optional[str] = None openrouter_key: Optional[str] = None huggingface_key: Optional[str] = None vertex_project: Optional[str] = None diff --git a/litellm/llms/ollama_chat.py b/litellm/llms/ollama_chat.py index d442ba5aae..aea00a303f 100644 --- a/litellm/llms/ollama_chat.py +++ b/litellm/llms/ollama_chat.py @@ -184,6 +184,7 @@ class OllamaChatConfig: # ollama implementation def get_ollama_response( api_base="http://localhost:11434", + api_key: Optional[str] = None, model="llama2", messages=None, optional_params=None, @@ -236,6 +237,7 @@ def get_ollama_response( if stream == True: response = ollama_async_streaming( url=url, + api_key=api_key, data=data, model_response=model_response, encoding=encoding, @@ -244,6 +246,7 @@ def get_ollama_response( else: response = ollama_acompletion( url=url, + api_key=api_key, data=data, model_response=model_response, encoding=encoding, @@ -252,12 +255,17 @@ def get_ollama_response( ) return response elif stream == True: - return ollama_completion_stream(url=url, data=data, logging_obj=logging_obj) + return ollama_completion_stream( + url=url, api_key=api_key, data=data, logging_obj=logging_obj + ) - response = requests.post( - url=f"{url}", - json=data, - ) + _request = { + "url": f"{url}", + "json": data, + } + if api_key is not None: + _request["headers"] = "Bearer {}".format(api_key) + response = requests.post(**_request) # type: ignore if response.status_code != 200: raise OllamaError(status_code=response.status_code, message=response.text) @@ -307,10 +315,16 @@ def get_ollama_response( return model_response -def ollama_completion_stream(url, data, logging_obj): - with httpx.stream( - url=url, json=data, method="POST", timeout=litellm.request_timeout - ) as response: +def ollama_completion_stream(url, api_key, data, logging_obj): + _request = { + "url": f"{url}", + "json": data, + "method": "POST", + "timeout": litellm.request_timeout, + } + if api_key is not None: + _request["headers"] = "Bearer {}".format(api_key) + with httpx.stream(**_request) as response: try: if response.status_code != 200: raise OllamaError( @@ -329,12 +343,20 @@ def ollama_completion_stream(url, data, logging_obj): raise e -async def ollama_async_streaming(url, data, model_response, encoding, logging_obj): +async def ollama_async_streaming( + url, api_key, data, model_response, encoding, logging_obj +): try: client = httpx.AsyncClient() - async with client.stream( - url=f"{url}", json=data, method="POST", timeout=litellm.request_timeout - ) as response: + _request = { + "url": f"{url}", + "json": data, + "method": "POST", + "timeout": litellm.request_timeout, + } + if api_key is not None: + _request["headers"] = "Bearer {}".format(api_key) + async with client.stream(**_request) as response: if response.status_code != 200: raise OllamaError( status_code=response.status_code, message=response.text @@ -353,13 +375,25 @@ async def ollama_async_streaming(url, data, model_response, encoding, logging_ob async def ollama_acompletion( - url, data, model_response, encoding, logging_obj, function_name + url, + api_key: Optional[str], + data, + model_response, + encoding, + logging_obj, + function_name, ): data["stream"] = False try: timeout = aiohttp.ClientTimeout(total=litellm.request_timeout) # 10 minutes async with aiohttp.ClientSession(timeout=timeout) as session: - resp = await session.post(url, json=data) + _request = { + "url": f"{url}", + "json": data, + } + if api_key is not None: + _request["headers"] = "Bearer {}".format(api_key) + resp = await session.post(**_request) if resp.status != 200: text = await resp.text() diff --git a/litellm/main.py b/litellm/main.py index b1e75f744c..65696b3c0c 100644 --- a/litellm/main.py +++ b/litellm/main.py @@ -1941,9 +1941,16 @@ def completion( or "http://localhost:11434" ) + api_key = ( + api_key + or litellm.ollama_key + or os.environ.get("OLLAMA_API_KEY") + or litellm.api_key + ) ## LOGGING generator = ollama_chat.get_ollama_response( api_base, + api_key, model, messages, optional_params, diff --git a/litellm/proxy/_new_secret_config.yaml b/litellm/proxy/_new_secret_config.yaml index ca8b4c5393..0f7c24576e 100644 --- a/litellm/proxy/_new_secret_config.yaml +++ b/litellm/proxy/_new_secret_config.yaml @@ -31,12 +31,12 @@ litellm_settings: upperbound_key_generate_params: max_budget: os.environ/LITELLM_UPPERBOUND_KEYS_MAX_BUDGET -# router_settings: -# routing_strategy: usage-based-routing-v2 -# redis_host: os.environ/REDIS_HOST -# redis_password: os.environ/REDIS_PASSWORD -# redis_port: os.environ/REDIS_PORT -# enable_pre_call_checks: True +router_settings: + routing_strategy: usage-based-routing-v2 + redis_host: os.environ/REDIS_HOST + redis_password: os.environ/REDIS_PASSWORD + redis_port: os.environ/REDIS_PORT + enable_pre_call_checks: True general_settings: master_key: sk-1234 From f411443e585245118a5411e6efcc7b18e51bce76 Mon Sep 17 00:00:00 2001 From: Josh Mandel Date: Fri, 19 Apr 2024 16:09:44 -0500 Subject: [PATCH 04/12] fix: Stream completion responses from anthropic. (Fix 3129) --- litellm/llms/anthropic.py | 3 ++- litellm/llms/custom_httpx/http_handler.py | 7 +++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/litellm/llms/anthropic.py b/litellm/llms/anthropic.py index d836ed8db5..24d889b0f4 100644 --- a/litellm/llms/anthropic.py +++ b/litellm/llms/anthropic.py @@ -258,8 +258,9 @@ class AnthropicChatCompletion(BaseLLM): self.async_handler = AsyncHTTPHandler( timeout=httpx.Timeout(timeout=600.0, connect=5.0) ) + data["stream"] = True response = await self.async_handler.post( - api_base, headers=headers, data=json.dumps(data) + api_base, headers=headers, data=json.dumps(data), stream=True ) if response.status_code != 200: diff --git a/litellm/llms/custom_httpx/http_handler.py b/litellm/llms/custom_httpx/http_handler.py index dd03e7dbec..3ab8577236 100644 --- a/litellm/llms/custom_httpx/http_handler.py +++ b/litellm/llms/custom_httpx/http_handler.py @@ -41,13 +41,16 @@ class AsyncHTTPHandler: data: Optional[Union[dict, str]] = None, # type: ignore params: Optional[dict] = None, headers: Optional[dict] = None, + stream: Optional[bool] = False ): - response = await self.client.post( + req = self.client.build_request( + "POST", url, data=data, # type: ignore params=params, - headers=headers, + headers=headers ) + response = await self.client.send(req, stream=stream) return response def __del__(self) -> None: From 410d1f2d2cbcaa54cdaef2c9cf417b429a9ba562 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Fri, 19 Apr 2024 14:26:58 -0700 Subject: [PATCH 05/12] langfuse - log team alias --- litellm/integrations/langfuse.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/litellm/integrations/langfuse.py b/litellm/integrations/langfuse.py index 6e26bb0230..d1d9ff8ae9 100644 --- a/litellm/integrations/langfuse.py +++ b/litellm/integrations/langfuse.py @@ -280,9 +280,10 @@ class LangFuseLogger: for key, value in metadata.items(): # generate langfuse tags if key in [ - "user_api_key", + "user_api_key_alias", "user_api_key_user_id", "user_api_key_team_id", + "user_api_key_team_alias", "semantic-similarity", ]: tags.append(f"{key}:{value}") From 12bf4346ec9441b4a7fabac24d0901bf186bc708 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Fri, 19 Apr 2024 14:34:38 -0700 Subject: [PATCH 06/12] feat - add llama3 on groq --- ...odel_prices_and_context_window_backup.json | 20 +++++++++++++++++++ model_prices_and_context_window.json | 20 +++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index eedcaa4932..113f9413fd 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -735,6 +735,26 @@ "mode": "chat", "supports_function_calling": true }, + "groq/llama3-8b-8192": { + "max_tokens": 8192, + "max_input_tokens": 8192, + "max_output_tokens": 8192, + "input_cost_per_token": 0.00000010, + "output_cost_per_token": 0.00000010, + "litellm_provider": "groq", + "mode": "chat", + "supports_function_calling": true + }, + "groq/llama3-70b-8192": { + "max_tokens": 8192, + "max_input_tokens": 8192, + "max_output_tokens": 8192, + "input_cost_per_token": 0.00000064, + "output_cost_per_token": 0.00000080, + "litellm_provider": "groq", + "mode": "chat", + "supports_function_calling": true + }, "groq/mixtral-8x7b-32768": { "max_tokens": 32768, "max_input_tokens": 32768, diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index eedcaa4932..113f9413fd 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -735,6 +735,26 @@ "mode": "chat", "supports_function_calling": true }, + "groq/llama3-8b-8192": { + "max_tokens": 8192, + "max_input_tokens": 8192, + "max_output_tokens": 8192, + "input_cost_per_token": 0.00000010, + "output_cost_per_token": 0.00000010, + "litellm_provider": "groq", + "mode": "chat", + "supports_function_calling": true + }, + "groq/llama3-70b-8192": { + "max_tokens": 8192, + "max_input_tokens": 8192, + "max_output_tokens": 8192, + "input_cost_per_token": 0.00000064, + "output_cost_per_token": 0.00000080, + "litellm_provider": "groq", + "mode": "chat", + "supports_function_calling": true + }, "groq/mixtral-8x7b-32768": { "max_tokens": 32768, "max_input_tokens": 32768, From e8de984b389510956dee2b5291912924aeaba776 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Fri, 19 Apr 2024 14:34:45 -0700 Subject: [PATCH 07/12] docs - add groq llama3 --- docs/my-website/docs/providers/groq.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/my-website/docs/providers/groq.md b/docs/my-website/docs/providers/groq.md index 8443387a5f..da453c3ceb 100644 --- a/docs/my-website/docs/providers/groq.md +++ b/docs/my-website/docs/providers/groq.md @@ -48,6 +48,8 @@ We support ALL Groq models, just set `groq/` as a prefix when sending completion | Model Name | Function Call | |--------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| llama3-8b-8192 | `completion(model="groq/llama3-8b-8192", messages)` | +| llama3-70b-8192 | `completion(model="groq/llama3-70b-8192", messages)` | | llama2-70b-4096 | `completion(model="groq/llama2-70b-4096", messages)` | | mixtral-8x7b-32768 | `completion(model="groq/mixtral-8x7b-32768", messages)` | | gemma-7b-it | `completion(model="groq/gemma-7b-it", messages)` | From 3167c9da9f6bfb4e06f95ed69496689cc0967e58 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Fri, 19 Apr 2024 14:43:41 -0700 Subject: [PATCH 08/12] fix - use user_api_key_team_alias as the default tag on langfuse --- litellm/integrations/langfuse.py | 1 - 1 file changed, 1 deletion(-) diff --git a/litellm/integrations/langfuse.py b/litellm/integrations/langfuse.py index d1d9ff8ae9..2601cbf6e6 100644 --- a/litellm/integrations/langfuse.py +++ b/litellm/integrations/langfuse.py @@ -282,7 +282,6 @@ class LangFuseLogger: if key in [ "user_api_key_alias", "user_api_key_user_id", - "user_api_key_team_id", "user_api_key_team_alias", "semantic-similarity", ]: From 559a312c9cfc752ea76ccd2bb0c6aab516664ee6 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Fri, 19 Apr 2024 15:07:13 -0700 Subject: [PATCH 09/12] ui - show teams as dropdown in create user flow --- ui/litellm-dashboard/src/app/page.tsx | 1 + .../src/components/create_user_button.tsx | 42 +++++++------------ .../src/components/view_users.tsx | 4 +- 3 files changed, 20 insertions(+), 27 deletions(-) diff --git a/ui/litellm-dashboard/src/app/page.tsx b/ui/litellm-dashboard/src/app/page.tsx index 7bd0656236..0a7cc6403f 100644 --- a/ui/litellm-dashboard/src/app/page.tsx +++ b/ui/litellm-dashboard/src/app/page.tsx @@ -145,6 +145,7 @@ const CreateKeyPage = () => { userRole={userRole} token={token} keys={keys} + teams={teams} accessToken={accessToken} setKeys={setKeys} /> diff --git a/ui/litellm-dashboard/src/components/create_user_button.tsx b/ui/litellm-dashboard/src/components/create_user_button.tsx index 94ddcabb04..e2c467be49 100644 --- a/ui/litellm-dashboard/src/components/create_user_button.tsx +++ b/ui/litellm-dashboard/src/components/create_user_button.tsx @@ -7,9 +7,10 @@ const { Option } = Select; interface CreateuserProps { userID: string; accessToken: string; + teams: any[] | null; } -const Createuser: React.FC = ({ userID, accessToken }) => { +const Createuser: React.FC = ({ userID, accessToken, teams }) => { const [form] = Form.useForm(); const [isModalVisible, setIsModalVisible] = useState(false); const [apiuser, setApiuser] = useState(null); @@ -90,38 +91,27 @@ const Createuser: React.FC = ({ userID, accessToken }) => { wrapperCol={{ span: 16 }} labelAlign="left" > - - + + - - - - - - - - - - - - - - - - - diff --git a/ui/litellm-dashboard/src/components/view_users.tsx b/ui/litellm-dashboard/src/components/view_users.tsx index 7270b0d22d..d9fa5f7844 100644 --- a/ui/litellm-dashboard/src/components/view_users.tsx +++ b/ui/litellm-dashboard/src/components/view_users.tsx @@ -36,6 +36,7 @@ interface ViewUserDashboardProps { keys: any[] | null; userRole: string | null; userID: string | null; + teams: any[] | null; setKeys: React.Dispatch>; } @@ -45,6 +46,7 @@ const ViewUserDashboard: React.FC = ({ keys, userRole, userID, + teams, setKeys, }) => { const [userData, setUserData] = useState(null); @@ -151,7 +153,7 @@ const ViewUserDashboard: React.FC = ({ return (
- + From 5d9f6282ce839adaadd42d71ab981cff2199646a Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Fri, 19 Apr 2024 15:17:50 -0700 Subject: [PATCH 10/12] create_user using user_email --- .../src/components/create_user_button.tsx | 18 +++++++----------- .../src/components/networking.tsx | 2 +- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/ui/litellm-dashboard/src/components/create_user_button.tsx b/ui/litellm-dashboard/src/components/create_user_button.tsx index e2c467be49..e2da08a63a 100644 --- a/ui/litellm-dashboard/src/components/create_user_button.tsx +++ b/ui/litellm-dashboard/src/components/create_user_button.tsx @@ -60,7 +60,7 @@ const Createuser: React.FC = ({ userID, accessToken, teams }) = message.info("Making API Call"); setIsModalVisible(true); console.log("formValues in create user:", formValues); - const response = await userCreateCall(accessToken, userID, formValues); + const response = await userCreateCall(accessToken, null, formValues); console.log("user create Response:", response); setApiuser(response["key"]); message.success("API user Created"); @@ -122,23 +122,19 @@ const Createuser: React.FC = ({ userID, accessToken, teams }) = {apiuser && (

- Please save this secret user somewhere safe and accessible. For - security reasons, you will not be able to view it again{" "} - through your LiteLLM account. If you lose this secret user, you will - need to generate a new one. -

-

- {apiuser != null - ? `API user: ${apiuser}` - : "User being created, this might take 30s"} + User has been created to access your proxy. Please Ask them to Log In.

+

+ +

Note: This Feature is only supported through SSO on the Admin UI

+
)}
diff --git a/ui/litellm-dashboard/src/components/networking.tsx b/ui/litellm-dashboard/src/components/networking.tsx index 46ec87e399..4b961ca347 100644 --- a/ui/litellm-dashboard/src/components/networking.tsx +++ b/ui/litellm-dashboard/src/components/networking.tsx @@ -158,7 +158,7 @@ export const keyCreateCall = async ( export const userCreateCall = async ( accessToken: string, - userID: string, + userID: string | null, formValues: Record // Assuming formValues is an object ) => { try { From 5613f9b30abb47048bc51a7a6f3315b8a1ea1eff Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Fri, 19 Apr 2024 15:26:29 -0700 Subject: [PATCH 11/12] UI - invite user flow --- .../src/components/create_user_button.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ui/litellm-dashboard/src/components/create_user_button.tsx b/ui/litellm-dashboard/src/components/create_user_button.tsx index e2da08a63a..b3fbfd5d77 100644 --- a/ui/litellm-dashboard/src/components/create_user_button.tsx +++ b/ui/litellm-dashboard/src/components/create_user_button.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect } from "react"; import { Button, Modal, Form, Input, message, Select, InputNumber } from "antd"; -import { Button as Button2 } from "@tremor/react"; +import { Button as Button2, Text } from "@tremor/react"; import { userCreateCall, modelAvailableCall } from "./networking"; const { Option } = Select; @@ -74,16 +74,18 @@ const Createuser: React.FC = ({ userID, accessToken, teams }) = return (
setIsModalVisible(true)}> - + Create New User + + Invite User + Invite a user to login to the Admin UI and create Keys + Note: SSO Setup Required for this
Date: Fri, 19 Apr 2024 15:45:24 -0700 Subject: [PATCH 12/12] fix(http_handler.py): fix linting error --- litellm/llms/custom_httpx/http_handler.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/litellm/llms/custom_httpx/http_handler.py b/litellm/llms/custom_httpx/http_handler.py index 3ab8577236..7c7d4938a4 100644 --- a/litellm/llms/custom_httpx/http_handler.py +++ b/litellm/llms/custom_httpx/http_handler.py @@ -41,14 +41,10 @@ class AsyncHTTPHandler: data: Optional[Union[dict, str]] = None, # type: ignore params: Optional[dict] = None, headers: Optional[dict] = None, - stream: Optional[bool] = False + stream: bool = False, ): req = self.client.build_request( - "POST", - url, - data=data, # type: ignore - params=params, - headers=headers + "POST", url, data=data, params=params, headers=headers # type: ignore ) response = await self.client.send(req, stream=stream) return response