mirror of
https://github.com/crewAIInc/crewAI.git
synced 2025-12-16 04:18:35 +00:00
fix: support to define MPC connection timeout on CrewBase instance (#3465)
* fix: support to define MPC connection timeout on CrewBase instance * fix: resolve linter issues * chore: ignore specific rule N802 on CrewBase class * fix: ignore untyped import
This commit is contained in:
@@ -142,7 +142,7 @@ with MCPServerAdapter(server_params, "tool_name", connect_timeout=60) as mcp_too
|
|||||||
|
|
||||||
## Using with CrewBase
|
## Using with CrewBase
|
||||||
|
|
||||||
To use MCPServer tools within a CrewBase class, use the `mcp_tools` method. Server configurations should be provided via the mcp_server_params attribute. You can pass either a single configuration or a list of multiple server configurations.
|
To use MCPServer tools within a CrewBase class, use the `get_mcp_tools` method. Server configurations should be provided via the `mcp_server_params` attribute. You can pass either a single configuration or a list of multiple server configurations.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
@CrewBase
|
@CrewBase
|
||||||
@@ -175,6 +175,34 @@ class CrewWithMCP:
|
|||||||
# ... rest of your crew setup ...
|
# ... rest of your crew setup ...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Connection Timeout Configuration
|
||||||
|
|
||||||
|
You can configure the connection timeout for MCP servers by setting the `mcp_connect_timeout` class attribute. If no timeout is specified, it defaults to 30 seconds.
|
||||||
|
|
||||||
|
```python
|
||||||
|
@CrewBase
|
||||||
|
class CrewWithMCP:
|
||||||
|
mcp_server_params = [...]
|
||||||
|
mcp_connect_timeout = 60 # 60 seconds timeout for all MCP connections
|
||||||
|
|
||||||
|
@agent
|
||||||
|
def your_agent(self):
|
||||||
|
return Agent(config=self.agents_config["your_agent"], tools=self.get_mcp_tools())
|
||||||
|
```
|
||||||
|
|
||||||
|
```python
|
||||||
|
@CrewBase
|
||||||
|
class CrewWithDefaultTimeout:
|
||||||
|
mcp_server_params = [...]
|
||||||
|
# No mcp_connect_timeout specified - uses default 30 seconds
|
||||||
|
|
||||||
|
@agent
|
||||||
|
def your_agent(self):
|
||||||
|
return Agent(config=self.agents_config["your_agent"], tools=self.get_mcp_tools())
|
||||||
|
```
|
||||||
|
|
||||||
|
### Filtering Tools
|
||||||
|
|
||||||
You can filter which tools are available to your agent by passing a list of tool names to the `get_mcp_tools` method.
|
You can filter which tools are available to your agent by passing a list of tool names to the `get_mcp_tools` method.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
@@ -186,6 +214,22 @@ def another_agent(self):
|
|||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The timeout configuration applies to all MCP tool calls within the crew:
|
||||||
|
|
||||||
|
```python
|
||||||
|
@CrewBase
|
||||||
|
class CrewWithCustomTimeout:
|
||||||
|
mcp_server_params = [...]
|
||||||
|
mcp_connect_timeout = 90 # 90 seconds timeout for all MCP connections
|
||||||
|
|
||||||
|
@agent
|
||||||
|
def filtered_agent(self):
|
||||||
|
return Agent(
|
||||||
|
config=self.agents_config["your_agent"],
|
||||||
|
tools=self.get_mcp_tools("tool_1", "tool_2") # specific tools with custom timeout
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
## Explore MCP Integrations
|
## Explore MCP Integrations
|
||||||
|
|
||||||
<CardGroup cols={2}>
|
<CardGroup cols={2}>
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ mode: "wide"
|
|||||||
|
|
||||||
## 개요
|
## 개요
|
||||||
|
|
||||||
[Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP)는 AI 에이전트가 MCP 서버로 알려진 외부 서비스와 통신함으로써 LLM에 컨텍스트를 제공할 수 있도록 표준화된 방식을 제공합니다.
|
[Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP)는 AI 에이전트가 MCP 서버로 알려진 외부 서비스와 통신함으로써 LLM에 컨텍스트를 제공할 수 있도록 표준화된 방식을 제공합니다.
|
||||||
`crewai-tools` 라이브러리는 CrewAI의 기능을 확장하여, 이러한 MCP 서버에서 제공하는 툴을 에이전트에 원활하게 통합할 수 있도록 해줍니다.
|
`crewai-tools` 라이브러리는 CrewAI의 기능을 확장하여, 이러한 MCP 서버에서 제공하는 툴을 에이전트에 원활하게 통합할 수 있도록 해줍니다.
|
||||||
이를 통해 여러분의 crew는 방대한 기능 에코시스템에 접근할 수 있습니다.
|
이를 통해 여러분의 crew는 방대한 기능 에코시스템에 접근할 수 있습니다.
|
||||||
|
|
||||||
현재 다음과 같은 전송 메커니즘을 지원합니다:
|
현재 다음과 같은 전송 메커니즘을 지원합니다:
|
||||||
@@ -142,7 +142,7 @@ with MCPServerAdapter(server_params, "tool_name", connect_timeout=60) as mcp_too
|
|||||||
|
|
||||||
## CrewBase와 함께 사용하기
|
## CrewBase와 함께 사용하기
|
||||||
|
|
||||||
CrewBase 클래스 내에서 MCPServer 도구를 사용하려면 `mcp_tools` 메서드를 사용하세요. 서버 구성은 mcp_server_params 속성을 통해 제공되어야 합니다. 단일 구성 또는 여러 서버 구성을 리스트 형태로 전달할 수 있습니다.
|
CrewBase 클래스 내에서 MCPServer 도구를 사용하려면 `get_mcp_tools` 메서드를 사용하세요. 서버 구성은 `mcp_server_params` 속성을 통해 제공되어야 합니다. 단일 구성 또는 여러 서버 구성을 리스트 형태로 전달할 수 있습니다.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
@CrewBase
|
@CrewBase
|
||||||
@@ -175,6 +175,34 @@ class CrewWithMCP:
|
|||||||
# ... 나머지 crew 설정 ...
|
# ... 나머지 crew 설정 ...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 연결 타임아웃 구성
|
||||||
|
|
||||||
|
`mcp_connect_timeout` 클래스 속성을 설정하여 MCP 서버의 연결 타임아웃을 구성할 수 있습니다. 타임아웃을 지정하지 않으면 기본값으로 30초가 사용됩니다.
|
||||||
|
|
||||||
|
```python
|
||||||
|
@CrewBase
|
||||||
|
class CrewWithMCP:
|
||||||
|
mcp_server_params = [...]
|
||||||
|
mcp_connect_timeout = 60 # 모든 MCP 연결에 60초 타임아웃
|
||||||
|
|
||||||
|
@agent
|
||||||
|
def your_agent(self):
|
||||||
|
return Agent(config=self.agents_config["your_agent"], tools=self.get_mcp_tools())
|
||||||
|
```
|
||||||
|
|
||||||
|
```python
|
||||||
|
@CrewBase
|
||||||
|
class CrewWithDefaultTimeout:
|
||||||
|
mcp_server_params = [...]
|
||||||
|
# mcp_connect_timeout 지정하지 않음 - 기본 30초 사용
|
||||||
|
|
||||||
|
@agent
|
||||||
|
def your_agent(self):
|
||||||
|
return Agent(config=self.agents_config["your_agent"], tools=self.get_mcp_tools())
|
||||||
|
```
|
||||||
|
|
||||||
|
### 도구 필터링
|
||||||
|
|
||||||
`get_mcp_tools` 메서드에 도구 이름의 리스트를 전달하여, 에이전트에 제공되는 도구를 필터링할 수 있습니다.
|
`get_mcp_tools` 메서드에 도구 이름의 리스트를 전달하여, 에이전트에 제공되는 도구를 필터링할 수 있습니다.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
@@ -186,6 +214,22 @@ def another_agent(self):
|
|||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
타임아웃 구성은 crew 내의 모든 MCP 도구 호출에 적용됩니다:
|
||||||
|
|
||||||
|
```python
|
||||||
|
@CrewBase
|
||||||
|
class CrewWithCustomTimeout:
|
||||||
|
mcp_server_params = [...]
|
||||||
|
mcp_connect_timeout = 90 # 모든 MCP 연결에 90초 타임아웃
|
||||||
|
|
||||||
|
@agent
|
||||||
|
def filtered_agent(self):
|
||||||
|
return Agent(
|
||||||
|
config=self.agents_config["your_agent"],
|
||||||
|
tools=self.get_mcp_tools("tool_1", "tool_2") # 사용자 지정 타임아웃으로 특정 도구
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
## MCP 통합 탐색
|
## MCP 통합 탐색
|
||||||
|
|
||||||
<CardGroup cols={2}>
|
<CardGroup cols={2}>
|
||||||
@@ -261,4 +305,4 @@ SSE 전송은 적절하게 보안되지 않은 경우 DNS 리바인딩 공격에
|
|||||||
|
|
||||||
### 제한 사항
|
### 제한 사항
|
||||||
* **지원되는 프리미티브**: 현재 `MCPServerAdapter`는 주로 MCP `tools`를 어댑팅하는 기능을 지원합니다. 다른 MCP 프리미티브(예: `prompts` 또는 `resources`)는 현재 이 어댑터를 통해 CrewAI 컴포넌트로 직접 통합되어 있지 않습니다.
|
* **지원되는 프리미티브**: 현재 `MCPServerAdapter`는 주로 MCP `tools`를 어댑팅하는 기능을 지원합니다. 다른 MCP 프리미티브(예: `prompts` 또는 `resources`)는 현재 이 어댑터를 통해 CrewAI 컴포넌트로 직접 통합되어 있지 않습니다.
|
||||||
* **출력 처리**: 어댑터는 일반적으로 MCP tool의 주요 텍스트 출력(예: `.content[0].text`)을 처리합니다. 복잡하거나 멀티모달 출력의 경우 이 패턴에 맞지 않으면 별도의 커스텀 처리가 필요할 수 있습니다.
|
* **출력 처리**: 어댑터는 일반적으로 MCP tool의 주요 텍스트 출력(예: `.content[0].text`)을 처리합니다. 복잡하거나 멀티모달 출력의 경우 이 패턴에 맞지 않으면 별도의 커스텀 처리가 필요할 수 있습니다.
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ with MCPServerAdapter(server_params, connect_timeout=60) as mcp_tools:
|
|||||||
|
|
||||||
## Usando com CrewBase
|
## Usando com CrewBase
|
||||||
|
|
||||||
Para usar ferramentas de servidores MCP dentro de uma classe CrewBase, utilize o método `mcp_tools`. As configurações dos servidores devem ser fornecidas via o atributo mcp_server_params. Você pode passar uma configuração única ou uma lista com múltiplas configurações.
|
Para usar ferramentas de servidores MCP dentro de uma classe CrewBase, utilize o método `get_mcp_tools`. As configurações dos servidores devem ser fornecidas via o atributo `mcp_server_params`. Você pode passar uma configuração única ou uma lista com múltiplas configurações.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
@CrewBase
|
@CrewBase
|
||||||
@@ -146,10 +146,65 @@ class CrewWithMCP:
|
|||||||
|
|
||||||
@agent
|
@agent
|
||||||
def your_agent(self):
|
def your_agent(self):
|
||||||
return Agent(config=self.agents_config["your_agent"], tools=self.get_mcp_tools()) # você também pode filtrar quais ferramentas estarão disponíveis
|
return Agent(config=self.agents_config["your_agent"], tools=self.get_mcp_tools()) # obter todas as ferramentas disponíveis
|
||||||
|
|
||||||
# ... restante da configuração do seu crew ...
|
# ... restante da configuração do seu crew ...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Configuração de Timeout de Conexão
|
||||||
|
|
||||||
|
Você pode configurar o timeout de conexão para servidores MCP definindo o atributo de classe `mcp_connect_timeout`. Se nenhum timeout for especificado, o padrão é 30 segundos.
|
||||||
|
|
||||||
|
```python
|
||||||
|
@CrewBase
|
||||||
|
class CrewWithMCP:
|
||||||
|
mcp_server_params = [...]
|
||||||
|
mcp_connect_timeout = 60 # timeout de 60 segundos para todas as conexões MCP
|
||||||
|
|
||||||
|
@agent
|
||||||
|
def your_agent(self):
|
||||||
|
return Agent(config=self.agents_config["your_agent"], tools=self.get_mcp_tools())
|
||||||
|
```
|
||||||
|
|
||||||
|
```python
|
||||||
|
@CrewBase
|
||||||
|
class CrewWithDefaultTimeout:
|
||||||
|
mcp_server_params = [...]
|
||||||
|
# Nenhum mcp_connect_timeout especificado - usa padrão de 30 segundos
|
||||||
|
|
||||||
|
@agent
|
||||||
|
def your_agent(self):
|
||||||
|
return Agent(config=self.agents_config["your_agent"], tools=self.get_mcp_tools())
|
||||||
|
```
|
||||||
|
|
||||||
|
### Filtragem de Ferramentas
|
||||||
|
|
||||||
|
Você pode filtrar quais ferramentas estão disponíveis para seu agente passando uma lista de nomes de ferramentas para o método `get_mcp_tools`.
|
||||||
|
|
||||||
|
```python
|
||||||
|
@agent
|
||||||
|
def another_agent(self):
|
||||||
|
return Agent(
|
||||||
|
config=self.agents_config["your_agent"],
|
||||||
|
tools=self.get_mcp_tools("tool_1", "tool_2") # obter ferramentas específicas
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
A configuração de timeout se aplica a todas as chamadas de ferramentas MCP dentro do crew:
|
||||||
|
|
||||||
|
```python
|
||||||
|
@CrewBase
|
||||||
|
class CrewWithCustomTimeout:
|
||||||
|
mcp_server_params = [...]
|
||||||
|
mcp_connect_timeout = 90 # timeout de 90 segundos para todas as conexões MCP
|
||||||
|
|
||||||
|
@agent
|
||||||
|
def filtered_agent(self):
|
||||||
|
return Agent(
|
||||||
|
config=self.agents_config["your_agent"],
|
||||||
|
tools=self.get_mcp_tools("tool_1", "tool_2") # ferramentas específicas com timeout personalizado
|
||||||
|
)
|
||||||
|
```
|
||||||
## Explore Integrações MCP
|
## Explore Integrações MCP
|
||||||
|
|
||||||
<CardGroup cols={2}>
|
<CardGroup cols={2}>
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
import inspect
|
import inspect
|
||||||
import logging
|
import logging
|
||||||
|
from collections.abc import Callable
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Callable, Dict, TypeVar, cast, List
|
from typing import Any, TypeVar, cast
|
||||||
from crewai.tools import BaseTool
|
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
from crewai.tools import BaseTool
|
||||||
|
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
T = TypeVar("T", bound=type)
|
T = TypeVar("T", bound=type)
|
||||||
@@ -14,7 +16,7 @@ T = TypeVar("T", bound=type)
|
|||||||
"""Base decorator for creating crew classes with configuration and function management."""
|
"""Base decorator for creating crew classes with configuration and function management."""
|
||||||
|
|
||||||
|
|
||||||
def CrewBase(cls: T) -> T:
|
def CrewBase(cls: T) -> T: # noqa: N802
|
||||||
"""Wraps a class with crew functionality and configuration management."""
|
"""Wraps a class with crew functionality and configuration management."""
|
||||||
|
|
||||||
class WrappedClass(cls): # type: ignore
|
class WrappedClass(cls): # type: ignore
|
||||||
@@ -29,6 +31,7 @@ def CrewBase(cls: T) -> T:
|
|||||||
original_tasks_config_path = getattr(cls, "tasks_config", "config/tasks.yaml")
|
original_tasks_config_path = getattr(cls, "tasks_config", "config/tasks.yaml")
|
||||||
|
|
||||||
mcp_server_params: Any = getattr(cls, "mcp_server_params", None)
|
mcp_server_params: Any = getattr(cls, "mcp_server_params", None)
|
||||||
|
mcp_connect_timeout: int = getattr(cls, "mcp_connect_timeout", 30)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
@@ -86,15 +89,18 @@ def CrewBase(cls: T) -> T:
|
|||||||
import types
|
import types
|
||||||
return types.MethodType(_close_mcp_server, self)
|
return types.MethodType(_close_mcp_server, self)
|
||||||
|
|
||||||
def get_mcp_tools(self, *tool_names: list[str]) -> List[BaseTool]:
|
def get_mcp_tools(self, *tool_names: list[str]) -> list[BaseTool]:
|
||||||
if not self.mcp_server_params:
|
if not self.mcp_server_params:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
from crewai_tools import MCPServerAdapter
|
from crewai_tools import MCPServerAdapter # type: ignore[import-untyped]
|
||||||
|
|
||||||
adapter = getattr(self, '_mcp_server_adapter', None)
|
adapter = getattr(self, '_mcp_server_adapter', None)
|
||||||
if not adapter:
|
if not adapter:
|
||||||
self._mcp_server_adapter = MCPServerAdapter(self.mcp_server_params)
|
self._mcp_server_adapter = MCPServerAdapter(
|
||||||
|
self.mcp_server_params,
|
||||||
|
connect_timeout=self.mcp_connect_timeout
|
||||||
|
)
|
||||||
|
|
||||||
return self._mcp_server_adapter.tools.filter_by_names(tool_names or None)
|
return self._mcp_server_adapter.tools.filter_by_names(tool_names or None)
|
||||||
|
|
||||||
@@ -154,8 +160,8 @@ def CrewBase(cls: T) -> T:
|
|||||||
}
|
}
|
||||||
|
|
||||||
def _filter_functions(
|
def _filter_functions(
|
||||||
self, functions: Dict[str, Callable], attribute: str
|
self, functions: dict[str, Callable], attribute: str
|
||||||
) -> Dict[str, Callable]:
|
) -> dict[str, Callable]:
|
||||||
return {
|
return {
|
||||||
name: func
|
name: func
|
||||||
for name, func in functions.items()
|
for name, func in functions.items()
|
||||||
@@ -184,11 +190,11 @@ def CrewBase(cls: T) -> T:
|
|||||||
def _map_agent_variables(
|
def _map_agent_variables(
|
||||||
self,
|
self,
|
||||||
agent_name: str,
|
agent_name: str,
|
||||||
agent_info: Dict[str, Any],
|
agent_info: dict[str, Any],
|
||||||
llms: Dict[str, Callable],
|
llms: dict[str, Callable],
|
||||||
tool_functions: Dict[str, Callable],
|
tool_functions: dict[str, Callable],
|
||||||
cache_handler_functions: Dict[str, Callable],
|
cache_handler_functions: dict[str, Callable],
|
||||||
callbacks: Dict[str, Callable],
|
callbacks: dict[str, Callable],
|
||||||
) -> None:
|
) -> None:
|
||||||
if llm := agent_info.get("llm"):
|
if llm := agent_info.get("llm"):
|
||||||
try:
|
try:
|
||||||
@@ -245,13 +251,13 @@ def CrewBase(cls: T) -> T:
|
|||||||
def _map_task_variables(
|
def _map_task_variables(
|
||||||
self,
|
self,
|
||||||
task_name: str,
|
task_name: str,
|
||||||
task_info: Dict[str, Any],
|
task_info: dict[str, Any],
|
||||||
agents: Dict[str, Callable],
|
agents: dict[str, Callable],
|
||||||
tasks: Dict[str, Callable],
|
tasks: dict[str, Callable],
|
||||||
output_json_functions: Dict[str, Callable],
|
output_json_functions: dict[str, Callable],
|
||||||
tool_functions: Dict[str, Callable],
|
tool_functions: dict[str, Callable],
|
||||||
callback_functions: Dict[str, Callable],
|
callback_functions: dict[str, Callable],
|
||||||
output_pydantic_functions: Dict[str, Callable],
|
output_pydantic_functions: dict[str, Callable],
|
||||||
) -> None:
|
) -> None:
|
||||||
if context_list := task_info.get("context"):
|
if context_list := task_info.get("context"):
|
||||||
self.tasks_config[task_name]["context"] = [
|
self.tasks_config[task_name]["context"] = [
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from typing import List
|
from typing import Any, ClassVar
|
||||||
from unittest.mock import Mock, patch
|
from unittest.mock import Mock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from crewai.agent import Agent
|
from crewai.agent import Agent
|
||||||
@@ -44,8 +45,8 @@ class InternalCrew:
|
|||||||
agents_config = "config/agents.yaml"
|
agents_config = "config/agents.yaml"
|
||||||
tasks_config = "config/tasks.yaml"
|
tasks_config = "config/tasks.yaml"
|
||||||
|
|
||||||
agents: List[BaseAgent]
|
agents: list[BaseAgent]
|
||||||
tasks: List[Task]
|
tasks: list[Task]
|
||||||
|
|
||||||
@llm
|
@llm
|
||||||
def local_llm(self):
|
def local_llm(self):
|
||||||
@@ -89,7 +90,8 @@ class InternalCrew:
|
|||||||
|
|
||||||
@CrewBase
|
@CrewBase
|
||||||
class InternalCrewWithMCP(InternalCrew):
|
class InternalCrewWithMCP(InternalCrew):
|
||||||
mcp_server_params = {"host": "localhost", "port": 8000}
|
mcp_server_params: ClassVar[dict[str, Any]] = {"host": "localhost", "port": 8000}
|
||||||
|
mcp_connect_timeout = 120
|
||||||
|
|
||||||
@agent
|
@agent
|
||||||
def reporting_analyst(self):
|
def reporting_analyst(self):
|
||||||
@@ -200,8 +202,8 @@ def test_before_kickoff_with_none_input():
|
|||||||
def test_multiple_before_after_kickoff():
|
def test_multiple_before_after_kickoff():
|
||||||
@CrewBase
|
@CrewBase
|
||||||
class MultipleHooksCrew:
|
class MultipleHooksCrew:
|
||||||
agents: List[BaseAgent]
|
agents: list[BaseAgent]
|
||||||
tasks: List[Task]
|
tasks: list[Task]
|
||||||
|
|
||||||
agents_config = "config/agents.yaml"
|
agents_config = "config/agents.yaml"
|
||||||
tasks_config = "config/tasks.yaml"
|
tasks_config = "config/tasks.yaml"
|
||||||
@@ -284,4 +286,7 @@ def test_internal_crew_with_mcp():
|
|||||||
assert crew.reporting_analyst().tools == [simple_tool, another_simple_tool]
|
assert crew.reporting_analyst().tools == [simple_tool, another_simple_tool]
|
||||||
assert crew.researcher().tools == [simple_tool]
|
assert crew.researcher().tools == [simple_tool]
|
||||||
|
|
||||||
adapter_mock.assert_called_once_with({"host": "localhost", "port": 8000})
|
adapter_mock.assert_called_once_with(
|
||||||
|
{"host": "localhost", "port": 8000},
|
||||||
|
connect_timeout=120
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user