Repository: PrefectHQ/prefect

Issue:
Move ephemeral/hosted/cloud detection to PrefectClient
### First check

- [X] I am a contributor to the Prefect codebase

### Description

`PrefectClient` should know what type of server it's talking to.

### Impact

As part of introducing events to Prefect core we need to have a way to easily detect if we're currently connected to Cloud or to a local/ephemeral server. 

### Additional context

_No response_


Discussion:

Resolving patch:
diff --git a/src/prefect/cli/profile.py b/src/prefect/cli/profile.py
--- a/src/prefect/cli/profile.py
+++ b/src/prefect/cli/profile.py
@@ -17,7 +17,7 @@
 from prefect.cli._utilities import exit_with_error, exit_with_success
 from prefect.cli.cloud import CloudUnauthorizedError, get_cloud_client
 from prefect.cli.root import app
-from prefect.client import get_client
+from prefect.client.orchestration import ServerType, get_client
 from prefect.context import use_profile
 from prefect.utilities.collections import AutoEnum
 
@@ -276,7 +276,7 @@ async def check_orion_connection():
                 connect_error = await client.api_healthcheck()
                 if connect_error is not None:
                     return ConnectionStatus.ORION_ERROR
-                elif await client.using_ephemeral_app():
+                elif client.server_type == ServerType.EPHEMERAL:
                     # if the client is using an ephemeral Prefect app, inform the user
                     return ConnectionStatus.EPHEMERAL
                 else:
@@ -294,7 +294,7 @@ async def check_orion_connection():
             connect_error = await client.api_healthcheck()
             if connect_error is not None:
                 return ConnectionStatus.ORION_ERROR
-            elif await client.using_ephemeral_app():
+            elif client.server_type == ServerType.EPHEMERAL:
                 return ConnectionStatus.EPHEMERAL
             else:
                 return ConnectionStatus.ORION_CONNECTED
diff --git a/src/prefect/cli/root.py b/src/prefect/cli/root.py
--- a/src/prefect/cli/root.py
+++ b/src/prefect/cli/root.py
@@ -4,7 +4,6 @@
 import asyncio
 import platform
 import sys
-from typing import Optional
 
 import pendulum
 import rich.console
@@ -16,6 +15,7 @@
 import prefect.settings
 from prefect.cli._types import PrefectTyper
 from prefect.cli._utilities import with_cli_exception_handling
+from prefect.client.orchestration import ServerType
 from prefect.logging.configuration import setup_logging
 from prefect.settings import (
     PREFECT_CLI_COLORS,
@@ -101,11 +101,7 @@ async def version():
 
     from prefect.server.api.server import SERVER_API_VERSION
     from prefect.server.utilities.database import get_dialect
-    from prefect.settings import (
-        PREFECT_API_DATABASE_CONNECTION_URL,
-        PREFECT_API_URL,
-        PREFECT_CLOUD_API_URL,
-    )
+    from prefect.settings import PREFECT_API_DATABASE_CONNECTION_URL
 
     version_info = {
         "Version": prefect.__version__,
@@ -119,25 +115,18 @@ async def version():
         "Profile": prefect.context.get_settings_context().profile.name,
     }
 
-    is_ephemeral: Optional[bool] = None
+    server_type: str
+
     try:
         async with prefect.get_client() as client:
-            is_ephemeral = client._ephemeral_app is not None
-    except Exception as exc:
-        version_info["Server type"] = "<client error>"
-    else:
-        version_info["Server type"] = (
-            "ephemeral"
-            if is_ephemeral
-            else (
-                "cloud"
-                if PREFECT_API_URL.value().startswith(PREFECT_CLOUD_API_URL.value())
-                else "hosted"
-            )
-        )
+            server_type = client.server_type.value
+    except Exception:
+        server_type = "<client error>"
+
+    version_info["Server type"] = server_type.lower()
 
     # TODO: Consider adding an API route to retrieve this information?
-    if is_ephemeral:
+    if server_type == ServerType.EPHEMERAL.value:
         database = get_dialect(PREFECT_API_DATABASE_CONNECTION_URL.value()).name
         version_info["Server"] = {"Database": database}
         if database == "sqlite":
diff --git a/src/prefect/client/orchestration.py b/src/prefect/client/orchestration.py
--- a/src/prefect/client/orchestration.py
+++ b/src/prefect/client/orchestration.py
@@ -49,7 +49,9 @@
     PREFECT_API_REQUEST_TIMEOUT,
     PREFECT_API_TLS_INSECURE_SKIP_VERIFY,
     PREFECT_API_URL,
+    PREFECT_CLOUD_API_URL,
 )
+from prefect.utilities.collections import AutoEnum
 
 if TYPE_CHECKING:
     from prefect.flows import Flow
@@ -58,6 +60,12 @@
 from prefect.client.base import PrefectHttpxClient, app_lifespan_context
 
 
+class ServerType(AutoEnum):
+    EPHEMERAL = AutoEnum.auto()
+    SERVER = AutoEnum.auto()
+    CLOUD = AutoEnum.auto()
+
+
 def get_client(httpx_settings: dict = None) -> "PrefectClient":
     """
     Retrieve a HTTP client for communicating with the Prefect REST API.
@@ -137,9 +145,11 @@ def __init__(
         self._exit_stack = AsyncExitStack()
         self._ephemeral_app: Optional[FastAPI] = None
         self.manage_lifespan = True
+        self.server_type: ServerType
 
         # Only set if this client started the lifespan of the application
         self._ephemeral_lifespan: Optional[LifespanManager] = None
+
         self._closed = False
         self._started = False
 
@@ -174,9 +184,16 @@ def __init__(
             # client will use a standard HTTP/1.1 connection instead.
             httpx_settings.setdefault("http2", PREFECT_API_ENABLE_HTTP2.value())
 
+            self.server_type = (
+                ServerType.CLOUD
+                if api.startswith(PREFECT_CLOUD_API_URL.value())
+                else ServerType.SERVER
+            )
+
         # Connect to an in-process application
         elif isinstance(api, FastAPI):
             self._ephemeral_app = api
+            self.server_type = ServerType.EPHEMERAL
             httpx_settings.setdefault("app", self._ephemeral_app)
             httpx_settings.setdefault("base_url", "http://ephemeral-prefect/api")
 
@@ -251,9 +268,6 @@ async def hello(self) -> httpx.Response:
         """
         return await self._client.get("/hello")
 
-    async def using_ephemeral_app(self) -> bool:
-        return self._ephemeral_app is not None
-
     async def create_flow(self, flow: "Flow") -> UUID:
         """
         Create a flow in the Prefect API.


Test patch:
diff --git a/tests/cli/test_version.py b/tests/cli/test_version.py
--- a/tests/cli/test_version.py
+++ b/tests/cli/test_version.py
@@ -19,9 +19,9 @@ def test_version_ephemeral_server_type():
 
 
 @pytest.mark.usefixtures("use_hosted_orion")
-def test_version_hosted_server_type():
+def test_version_server_server_type():
     invoke_and_assert(
-        ["version"], expected_output_contains="Server type:         hosted"
+        ["version"], expected_output_contains="Server type:         server"
     )
 
 
@@ -110,6 +110,6 @@ def test_correct_output_non_ephemeral_server_type():
 Built:               {built.to_day_datetime_string()}
 OS/Arch:             {sys.platform}/{platform.machine()}
 Profile:             {profile.name}
-Server type:         hosted
+Server type:         server
 """,
     )
diff --git a/tests/client/test_orion_client.py b/tests/client/test_orion_client.py
--- a/tests/client/test_orion_client.py
+++ b/tests/client/test_orion_client.py
@@ -19,7 +19,7 @@
 import prefect.context
 import prefect.exceptions
 from prefect import flow, tags
-from prefect.client.orchestration import PrefectClient, get_client
+from prefect.client.orchestration import PrefectClient, ServerType, get_client
 from prefect.client.schemas import OrchestrationResult
 from prefect.client.utilities import inject_client
 from prefect.deprecated.data_documents import DataDocument
@@ -39,6 +39,7 @@
     PREFECT_API_KEY,
     PREFECT_API_TLS_INSECURE_SKIP_VERIFY,
     PREFECT_API_URL,
+    PREFECT_CLOUD_API_URL,
     temporary_settings,
 )
 from prefect.states import Completed, Pending, Running, Scheduled, State
@@ -1485,13 +1486,18 @@ async def test_delete_flow_run(orion_client, flow_run):
         await orion_client.delete_flow_run(flow_run.id)
 
 
-async def test_ephemeral_app_check(orion_client):
-    assert await orion_client.using_ephemeral_app()
+def test_server_type_ephemeral(orion_client):
+    assert orion_client.server_type == ServerType.EPHEMERAL
 
 
-async def test_ephemeral_app_check_when_using_hosted_orion(hosted_orion_api):
+async def test_server_type_server(hosted_orion_api):
     async with PrefectClient(hosted_orion_api) as orion_client:
-        assert (await orion_client.using_ephemeral_app()) is False
+        assert orion_client.server_type == ServerType.SERVER
+
+
+async def test_server_type_cloud():
+    async with PrefectClient(PREFECT_CLOUD_API_URL.value()) as orion_client:
+        assert orion_client.server_type == ServerType.CLOUD
 
 
 @pytest.mark.parametrize(