Repository: PrefectHQ/prefect

Issue:
Add version_group_id as input to create_flow_run
+ documentation in Cloud Concepts, for feature parity with our API


Discussion:

Resolving patch:
diff --git a/src/prefect/client/client.py b/src/prefect/client/client.py
--- a/src/prefect/client/client.py
+++ b/src/prefect/client/client.py
@@ -671,18 +671,20 @@ def create_project(self, project_name: str, project_description: str = None) ->
 
     def create_flow_run(
         self,
-        flow_id: str,
+        flow_id: str = None,
         context: dict = None,
         parameters: dict = None,
         scheduled_start_time: datetime.datetime = None,
         idempotency_key: str = None,
         run_name: str = None,
+        version_group_id: str = None,
     ) -> str:
         """
         Create a new flow run for the given flow id.  If `start_time` is not provided, the flow run will be scheduled to start immediately.
+        If both `flow_id` and `version_group_id` are provided, only the `flow_id` will be used.
 
         Args:
-            - flow_id (str): the id of the Flow you wish to schedule
+            - flow_id (str, optional): the id of the Flow you wish to schedule
             - context (dict, optional): the run context
             - parameters (dict, optional): a dictionary of parameter values to pass to the flow run
             - scheduled_start_time (datetime, optional): the time to schedule the execution for; if not provided, defaults to now
@@ -692,6 +694,8 @@ def create_flow_run(
                 An error will be raised if parameters or context are provided and don't match the original.
                 Each subsequent request will reset the TTL for 24 hours.
             - run_name (str, optional): The name assigned to this flow run
+            - version_group_id (str, optional): if provided, the unique unarchived flow within this version group will be scheduled
+                to run.  This input can be used as a stable API for running flows which are regularly updated.
 
         Returns:
             - str: the ID of the newly-created flow run
@@ -704,7 +708,12 @@ def create_flow_run(
                 "createFlowRun(input: $input)": {"flow_run": "id"}
             }
         }
-        inputs = dict(flowId=flow_id)
+        if not flow_id and not version_group_id:
+            raise ValueError("One of flow_id or version_group_id must be provided")
+
+        inputs = (
+            dict(flowId=flow_id) if flow_id else dict(versionGroupId=version_group_id)  # type: ignore
+        )
         if parameters is not None:
             inputs.update(parameters=parameters)  # type: ignore
         if context is not None:


Test patch:
diff --git a/tests/client/test_client.py b/tests/client/test_client.py
--- a/tests/client/test_client.py
+++ b/tests/client/test_client.py
@@ -645,3 +645,30 @@ def test_set_task_run_state_with_error(patch_post):
 
     with pytest.raises(ClientError, match="something went wrong"):
         client.set_task_run_state(task_run_id="76-salt", version=0, state=Pending())
+
+
+def test_create_flow_run_requires_flow_id_or_version_group_id():
+    with set_temporary_config(
+        {"cloud.graphql": "http://my-cloud.foo", "cloud.auth_token": "secret_token"}
+    ):
+        client = Client()
+
+    with pytest.raises(
+        ValueError, match="flow_id or version_group_id must be provided"
+    ):
+        client.create_flow_run()
+
+
+@pytest.mark.parametrize("kwargs", [dict(flow_id="blah"), dict(version_group_id="cat")])
+def test_create_flow_run_with_input(patch_post, kwargs):
+    response = {
+        "data": {"createFlowRun": {"flow_run": {"id": "FOO"}}},
+    }
+    post = patch_post(response)
+
+    with set_temporary_config(
+        {"cloud.graphql": "http://my-cloud.foo", "cloud.auth_token": "secret_token"}
+    ):
+        client = Client()
+
+    assert client.create_flow_run(**kwargs) == "FOO"