dnephin: Looks good, just one consideration for backwards compatibility.
diff --git a/docker/api/container.py b/docker/api/container.py
index ceac173f..8aa9aa2c 100644
--- a/docker/api/container.py
+++ b/docker/api/container.py
@@ -193,12 +193,14 @@ class ContainerApiMixin(object):
 
     @utils.check_resource
     def logs(self, container, stdout=True, stderr=True, stream=False,
-             timestamps=False, tail='all', since=None):
+             timestamps=False, tail='all', since=None, follow=None):
         if utils.compare_version('1.11', self._version) >= 0:
+            if follow is None:
+                follow = stream
             params = {'stderr': stderr and 1 or 0,
                       'stdout': stdout and 1 or 0,
                       'timestamps': timestamps and 1 or 0,
-                      'follow': stream and 1 or 0,
+                      'follow': follow and 1 or 0,
                       }
             if utils.compare_version('1.13', self._version) >= 0:
                 if tail != 'all' and (not isinstance(tail, int) or tail < 0):
diff --git a/docs/api.md b/docs/api.md
index 00ccabca..32952bf3 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -677,6 +677,7 @@ output as it happens.
 * timestamps (bool): Show timestamps
 * tail (str or int): Output specified number of lines at the end of logs: `"all"` or `number`. Default `"all"`
 * since (datetime or int): Show logs since a given datetime or integer epoch (in seconds)
+* follow (bool): Follow log output
 
 **Returns** (generator or str):
 

logs() separate param for stream and follow
From: https://github.com/docker/compose/pull/2720/files#r52222296

Current the `follow` param is set based on `stream`

I think `follow=True` does imply `stream=True`, but `stream=True` doesn't imply `follow=True`, you may still want to stream without following.
diff --git a/tests/integration/container_test.py b/tests/integration/container_test.py
index 1714599b..142299d3 100644
--- a/tests/integration/container_test.py
+++ b/tests/integration/container_test.py
@@ -666,7 +666,7 @@ Line2'''
         logs = self.client.logs(id, tail=1)
         self.assertEqual(logs, 'Line2\n'.encode(encoding='ascii'))
 
-    def test_logs_streaming(self):
+    def test_logs_streaming_and_follow(self):
         snippet = 'Flowering Nights (Sakuya Iyazoi)'
         container = self.client.create_container(
             BUSYBOX, 'echo {0}'.format(snippet)
@@ -675,7 +675,7 @@ Line2'''
         self.tmp_containers.append(id)
         self.client.start(id)
         logs = six.binary_type()
-        for chunk in self.client.logs(id, stream=True):
+        for chunk in self.client.logs(id, stream=True, follow=True):
             logs += chunk
 
         exitcode = self.client.wait(id)
diff --git a/tests/unit/container_test.py b/tests/unit/container_test.py
index c2b25734..d66eeede 100644
--- a/tests/unit/container_test.py
+++ b/tests/unit/container_test.py
@@ -1119,6 +1119,36 @@ class ContainerTest(DockerClientTest):
         )
 
     def test_log_streaming(self):
+        with mock.patch('docker.Client.inspect_container',
+                        fake_inspect_container):
+            self.client.logs(fake_api.FAKE_CONTAINER_ID, stream=True,
+                             follow=False)
+
+        fake_request.assert_called_with(
+            'GET',
+            url_prefix + 'containers/3cc2351ab11b/logs',
+            params={'timestamps': 0, 'follow': 0, 'stderr': 1, 'stdout': 1,
+                    'tail': 'all'},
+            timeout=DEFAULT_TIMEOUT_SECONDS,
+            stream=True
+        )
+
+    def test_log_following(self):
+        with mock.patch('docker.Client.inspect_container',
+                        fake_inspect_container):
+            self.client.logs(fake_api.FAKE_CONTAINER_ID, stream=False,
+                             follow=True)
+
+        fake_request.assert_called_with(
+            'GET',
+            url_prefix + 'containers/3cc2351ab11b/logs',
+            params={'timestamps': 0, 'follow': 1, 'stderr': 1, 'stdout': 1,
+                    'tail': 'all'},
+            timeout=DEFAULT_TIMEOUT_SECONDS,
+            stream=False
+        )
+
+    def test_log_following_backwards(self):
         with mock.patch('docker.Client.inspect_container',
                         fake_inspect_container):
             self.client.logs(fake_api.FAKE_CONTAINER_ID, stream=True)
@@ -1132,12 +1162,27 @@ class ContainerTest(DockerClientTest):
             stream=True
         )
 
+    def test_log_streaming_and_following(self):
+        with mock.patch('docker.Client.inspect_container',
+                        fake_inspect_container):
+            self.client.logs(fake_api.FAKE_CONTAINER_ID, stream=True,
+                             follow=True)
+
+        fake_request.assert_called_with(
+            'GET',
+            url_prefix + 'containers/3cc2351ab11b/logs',
+            params={'timestamps': 0, 'follow': 1, 'stderr': 1, 'stdout': 1,
+                    'tail': 'all'},
+            timeout=DEFAULT_TIMEOUT_SECONDS,
+            stream=True
+        )
+
     def test_log_tail(self):
 
         with mock.patch('docker.Client.inspect_container',
                         fake_inspect_container):
             self.client.logs(fake_api.FAKE_CONTAINER_ID, stream=False,
-                             tail=10)
+                             follow=False, tail=10)
 
         fake_request.assert_called_with(
             'GET',
@@ -1153,7 +1198,7 @@ class ContainerTest(DockerClientTest):
         with mock.patch('docker.Client.inspect_container',
                         fake_inspect_container):
             self.client.logs(fake_api.FAKE_CONTAINER_ID, stream=False,
-                             since=ts)
+                             follow=False, since=ts)
 
         fake_request.assert_called_with(
             'GET',
@@ -1170,7 +1215,7 @@ class ContainerTest(DockerClientTest):
         with mock.patch('docker.Client.inspect_container',
                         fake_inspect_container):
             self.client.logs(fake_api.FAKE_CONTAINER_ID, stream=False,
-                             since=time)
+                             follow=False, since=time)
 
         fake_request.assert_called_with(
             'GET',
@@ -1188,7 +1233,7 @@ class ContainerTest(DockerClientTest):
             with mock.patch('docker.Client._stream_raw_result',
                             m):
                 self.client.logs(fake_api.FAKE_CONTAINER_ID,
-                                 stream=True)
+                                 follow=True, stream=True)
 
         self.assertTrue(m.called)
         fake_request.assert_called_with(

pip install -e .
parse_log_pytest
requirements.txt
pytest
apt-get update apt-get install -y gcc
3.9
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
-e git+https://github.com/docker/docker-py.git@c3a66cc5999a5435b81769ac758d411d34c995c4#egg=docker_py
exceptiongroup==1.2.2
iniconfig==2.1.0
packaging==24.2
pluggy==1.5.0
pytest==8.3.5
requests==2.5.3
six==1.17.0
tomli==2.2.1
websocket_client==0.32.0

name: docker-py
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - exceptiongroup==1.2.2
      - iniconfig==2.1.0
      - packaging==24.2
      - pluggy==1.5.0
      - pytest==8.3.5
      - requests==2.5.3
      - six==1.17.0
      - tomli==2.2.1
      - websocket-client==0.32.0
prefix: /opt/conda/envs/docker-py

tests/unit/container_test.py::ContainerTest::test_log_following tests/unit/container_test.py::ContainerTest::test_log_since tests/unit/container_test.py::ContainerTest::test_log_since_with_datetime tests/unit/container_test.py::ContainerTest::test_log_streaming tests/unit/container_test.py::ContainerTest::test_log_streaming_and_following tests/unit/container_test.py::ContainerTest::test_log_tail tests/unit/container_test.py::ContainerTest::test_log_tty
tests/unit/container_test.py::StartContainerTest::test_start_container tests/unit/container_test.py::StartContainerTest::test_start_container_none tests/unit/container_test.py::StartContainerTest::test_start_container_privileged tests/unit/container_test.py::StartContainerTest::test_start_container_regression_573 tests/unit/container_test.py::StartContainerTest::test_start_container_with_binds_ro tests/unit/container_test.py::StartContainerTest::test_start_container_with_binds_rw tests/unit/container_test.py::StartContainerTest::test_start_container_with_dict_instead_of_id tests/unit/container_test.py::StartContainerTest::test_start_container_with_links tests/unit/container_test.py::StartContainerTest::test_start_container_with_links_as_list_of_tuples tests/unit/container_test.py::StartContainerTest::test_start_container_with_lxc_conf tests/unit/container_test.py::StartContainerTest::test_start_container_with_lxc_conf_compat tests/unit/container_test.py::StartContainerTest::test_start_container_with_multiple_links tests/unit/container_test.py::StartContainerTest::test_start_container_with_port_binds tests/unit/container_test.py::CreateContainerTest::test_create_container tests/unit/container_test.py::CreateContainerTest::test_create_container_empty_volumes_from tests/unit/container_test.py::CreateContainerTest::test_create_container_privileged tests/unit/container_test.py::CreateContainerTest::test_create_container_with_added_capabilities tests/unit/container_test.py::CreateContainerTest::test_create_container_with_binds tests/unit/container_test.py::CreateContainerTest::test_create_container_with_binds_list tests/unit/container_test.py::CreateContainerTest::test_create_container_with_binds_mode tests/unit/container_test.py::CreateContainerTest::test_create_container_with_binds_mode_and_ro_error tests/unit/container_test.py::CreateContainerTest::test_create_container_with_binds_ro tests/unit/container_test.py::CreateContainerTest::test_create_container_with_binds_rw tests/unit/container_test.py::CreateContainerTest::test_create_container_with_cgroup_parent tests/unit/container_test.py::CreateContainerTest::test_create_container_with_cpu_shares tests/unit/container_test.py::CreateContainerTest::test_create_container_with_cpuset tests/unit/container_test.py::CreateContainerTest::test_create_container_with_devices tests/unit/container_test.py::CreateContainerTest::test_create_container_with_dropped_capabilities tests/unit/container_test.py::CreateContainerTest::test_create_container_with_entrypoint tests/unit/container_test.py::CreateContainerTest::test_create_container_with_labels_dict tests/unit/container_test.py::CreateContainerTest::test_create_container_with_labels_list tests/unit/container_test.py::CreateContainerTest::test_create_container_with_links tests/unit/container_test.py::CreateContainerTest::test_create_container_with_links_as_list_of_tuples tests/unit/container_test.py::CreateContainerTest::test_create_container_with_lxc_conf tests/unit/container_test.py::CreateContainerTest::test_create_container_with_lxc_conf_compat tests/unit/container_test.py::CreateContainerTest::test_create_container_with_mac_address tests/unit/container_test.py::CreateContainerTest::test_create_container_with_mem_limit_as_int tests/unit/container_test.py::CreateContainerTest::test_create_container_with_mem_limit_as_string tests/unit/container_test.py::CreateContainerTest::test_create_container_with_mem_limit_as_string_with_g_unit tests/unit/container_test.py::CreateContainerTest::test_create_container_with_mem_limit_as_string_with_k_unit tests/unit/container_test.py::CreateContainerTest::test_create_container_with_mem_limit_as_string_with_m_unit tests/unit/container_test.py::CreateContainerTest::test_create_container_with_mem_limit_as_string_with_wrong_value tests/unit/container_test.py::CreateContainerTest::test_create_container_with_multiple_links tests/unit/container_test.py::CreateContainerTest::test_create_container_with_named_volume tests/unit/container_test.py::CreateContainerTest::test_create_container_with_port_binds tests/unit/container_test.py::CreateContainerTest::test_create_container_with_ports tests/unit/container_test.py::CreateContainerTest::test_create_container_with_restart_policy tests/unit/container_test.py::CreateContainerTest::test_create_container_with_stdin_open tests/unit/container_test.py::CreateContainerTest::test_create_container_with_stop_signal tests/unit/container_test.py::CreateContainerTest::test_create_container_with_volume_string tests/unit/container_test.py::CreateContainerTest::test_create_container_with_volumes_from tests/unit/container_test.py::CreateContainerTest::test_create_container_with_working_dir tests/unit/container_test.py::CreateContainerTest::test_create_named_container tests/unit/container_test.py::ContainerTest::test_container_stats tests/unit/container_test.py::ContainerTest::test_container_top tests/unit/container_test.py::ContainerTest::test_container_top_with_psargs tests/unit/container_test.py::ContainerTest::test_diff tests/unit/container_test.py::ContainerTest::test_diff_with_dict_instead_of_id tests/unit/container_test.py::ContainerTest::test_export tests/unit/container_test.py::ContainerTest::test_export_with_dict_instead_of_id tests/unit/container_test.py::ContainerTest::test_inspect_container tests/unit/container_test.py::ContainerTest::test_inspect_container_undefined_id tests/unit/container_test.py::ContainerTest::test_kill_container tests/unit/container_test.py::ContainerTest::test_kill_container_with_dict_instead_of_id tests/unit/container_test.py::ContainerTest::test_kill_container_with_signal tests/unit/container_test.py::ContainerTest::test_list_containers tests/unit/container_test.py::ContainerTest::test_log_following_backwards tests/unit/container_test.py::ContainerTest::test_logs tests/unit/container_test.py::ContainerTest::test_logs_with_dict_instead_of_id tests/unit/container_test.py::ContainerTest::test_pause_container tests/unit/container_test.py::ContainerTest::test_port tests/unit/container_test.py::ContainerTest::test_remove_container tests/unit/container_test.py::ContainerTest::test_remove_container_with_dict_instead_of_id tests/unit/container_test.py::ContainerTest::test_rename_container tests/unit/container_test.py::ContainerTest::test_resize_container tests/unit/container_test.py::ContainerTest::test_restart_container tests/unit/container_test.py::ContainerTest::test_restart_container_with_dict_instead_of_id tests/unit/container_test.py::ContainerTest::test_stop_container tests/unit/container_test.py::ContainerTest::test_stop_container_with_dict_instead_of_id tests/unit/container_test.py::ContainerTest::test_unpause_container tests/unit/container_test.py::ContainerTest::test_wait tests/unit/container_test.py::ContainerTest::test_wait_with_dict_instead_of_id
docs/api.md docker/api/container.py
docs/api.md docker/api/container.py