clams-python 1.7.3__tar.gz → 1.7.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. {clams_python-1.7.3 → clams_python-1.7.4}/CHANGELOG.md +9 -0
  2. {clams_python-1.7.3/clams_python.egg-info → clams_python-1.7.4}/PKG-INFO +1 -1
  3. {clams_python-1.7.3 → clams_python-1.7.4}/clams/app/__init__.py +77 -18
  4. {clams_python-1.7.3 → clams_python-1.7.4}/clams/develop/templates/app/Containerfile.template +1 -1
  5. {clams_python-1.7.3 → clams_python-1.7.4/clams_python.egg-info}/PKG-INFO +1 -1
  6. {clams_python-1.7.3 → clams_python-1.7.4}/clams_python.egg-info/scm_file_list.json +67 -67
  7. clams_python-1.7.4/clams_python.egg-info/scm_version.json +8 -0
  8. clams_python-1.7.4/container/ffmpeg-torch2.containerfile +13 -0
  9. clams_python-1.7.4/container/opencv4-torch2.containerfile +13 -0
  10. clams_python-1.7.4/container/torch2.containerfile +13 -0
  11. {clams_python-1.7.3 → clams_python-1.7.4}/documentation/app-baseclasses.rst +5 -0
  12. {clams_python-1.7.3 → clams_python-1.7.4}/documentation/clamsapp.rst +12 -0
  13. {clams_python-1.7.3 → clams_python-1.7.4}/documentation/introduction.rst +1 -1
  14. {clams_python-1.7.3 → clams_python-1.7.4}/documentation/runtime-params.rst +9 -1
  15. {clams_python-1.7.3 → clams_python-1.7.4}/documentation/target-versions.csv +1 -0
  16. {clams_python-1.7.3 → clams_python-1.7.4}/tests/test_clamsapp.py +77 -0
  17. {clams_python-1.7.3 → clams_python-1.7.4}/tests/test_envelope.py +13 -0
  18. clams_python-1.7.3/clams_python.egg-info/scm_version.json +0 -8
  19. clams_python-1.7.3/container/ffmpeg-torch2.containerfile +0 -6
  20. clams_python-1.7.3/container/opencv4-torch2.containerfile +0 -6
  21. clams_python-1.7.3/container/torch2.containerfile +0 -6
  22. {clams_python-1.7.3 → clams_python-1.7.4}/.github/workflows/codecov.yml +0 -0
  23. {clams_python-1.7.3 → clams_python-1.7.4}/.github/workflows/container.yml +0 -0
  24. {clams_python-1.7.3 → clams_python-1.7.4}/.github/workflows/containers.yml +0 -0
  25. {clams_python-1.7.3 → clams_python-1.7.4}/.github/workflows/issue-assign.yml +0 -0
  26. {clams_python-1.7.3 → clams_python-1.7.4}/.github/workflows/issue-close.yml +0 -0
  27. {clams_python-1.7.3 → clams_python-1.7.4}/.github/workflows/issue-infra-project.yml +0 -0
  28. {clams_python-1.7.3 → clams_python-1.7.4}/.github/workflows/publish.yml +0 -0
  29. {clams_python-1.7.3 → clams_python-1.7.4}/.github/workflows/release-check.yml +0 -0
  30. {clams_python-1.7.3 → clams_python-1.7.4}/.gitignore +0 -0
  31. {clams_python-1.7.3 → clams_python-1.7.4}/.pytype.cfg +0 -0
  32. {clams_python-1.7.3 → clams_python-1.7.4}/CONTRIBUTING.md +0 -0
  33. {clams_python-1.7.3 → clams_python-1.7.4}/LICENSE +0 -0
  34. {clams_python-1.7.3 → clams_python-1.7.4}/README.md +0 -0
  35. {clams_python-1.7.3 → clams_python-1.7.4}/build-tools/build.py +0 -0
  36. {clams_python-1.7.3 → clams_python-1.7.4}/build-tools/clean.py +0 -0
  37. {clams_python-1.7.3 → clams_python-1.7.4}/build-tools/docs.py +0 -0
  38. {clams_python-1.7.3 → clams_python-1.7.4}/build-tools/prep_release.py +0 -0
  39. {clams_python-1.7.3 → clams_python-1.7.4}/build-tools/publish.py +0 -0
  40. {clams_python-1.7.3 → clams_python-1.7.4}/build-tools/test.py +0 -0
  41. {clams_python-1.7.3 → clams_python-1.7.4}/clams/__init__.py +0 -0
  42. {clams_python-1.7.3 → clams_python-1.7.4}/clams/appmetadata/__init__.py +0 -0
  43. {clams_python-1.7.3 → clams_python-1.7.4}/clams/backends/__init__.py +0 -0
  44. {clams_python-1.7.3 → clams_python-1.7.4}/clams/backends/hf.py +0 -0
  45. {clams_python-1.7.3 → clams_python-1.7.4}/clams/develop/__init__.py +0 -0
  46. {clams_python-1.7.3 → clams_python-1.7.4}/clams/develop/templates/app/.dockerignore.template +0 -0
  47. {clams_python-1.7.3 → clams_python-1.7.4}/clams/develop/templates/app/.gitignore.template +0 -0
  48. {clams_python-1.7.3 → clams_python-1.7.4}/clams/develop/templates/app/LICENSE.template +0 -0
  49. {clams_python-1.7.3 → clams_python-1.7.4}/clams/develop/templates/app/README.md.template +0 -0
  50. {clams_python-1.7.3 → clams_python-1.7.4}/clams/develop/templates/app/app.py.template +0 -0
  51. {clams_python-1.7.3 → clams_python-1.7.4}/clams/develop/templates/app/cli.py.template +0 -0
  52. {clams_python-1.7.3 → clams_python-1.7.4}/clams/develop/templates/app/metadata.py.template +0 -0
  53. {clams_python-1.7.3 → clams_python-1.7.4}/clams/develop/templates/app/requirements.txt.template +0 -0
  54. {clams_python-1.7.3 → clams_python-1.7.4}/clams/develop/templates/gha/for-clams-team.md.template +0 -0
  55. {clams_python-1.7.3 → clams_python-1.7.4}/clams/develop/templates/gha/workflows/issue-apps-project.yml.template +0 -0
  56. {clams_python-1.7.3 → clams_python-1.7.4}/clams/develop/templates/gha/workflows/issue-assign.yml.template +0 -0
  57. {clams_python-1.7.3 → clams_python-1.7.4}/clams/develop/templates/gha/workflows/issue-close.yml.template +0 -0
  58. {clams_python-1.7.3 → clams_python-1.7.4}/clams/develop/templates/gha/workflows/publish.yml.template +0 -0
  59. {clams_python-1.7.3 → clams_python-1.7.4}/clams/develop/templates/utl-tf/__init__.py.template +0 -0
  60. {clams_python-1.7.3 → clams_python-1.7.4}/clams/develop/templates/utl-tf/timeframe.py.template +0 -0
  61. {clams_python-1.7.3 → clams_python-1.7.4}/clams/envelop/__init__.py +0 -0
  62. {clams_python-1.7.3 → clams_python-1.7.4}/clams/restify/__init__.py +0 -0
  63. {clams_python-1.7.3 → clams_python-1.7.4}/clams/serve/__init__.py +0 -0
  64. {clams_python-1.7.3 → clams_python-1.7.4}/clams/ver/__init__.py +0 -0
  65. {clams_python-1.7.3 → clams_python-1.7.4}/clams_python.egg-info/SOURCES.txt +0 -0
  66. {clams_python-1.7.3 → clams_python-1.7.4}/clams_python.egg-info/dependency_links.txt +0 -0
  67. {clams_python-1.7.3 → clams_python-1.7.4}/clams_python.egg-info/entry_points.txt +0 -0
  68. {clams_python-1.7.3 → clams_python-1.7.4}/clams_python.egg-info/requires.txt +0 -0
  69. {clams_python-1.7.3 → clams_python-1.7.4}/clams_python.egg-info/top_level.txt +0 -0
  70. {clams_python-1.7.3 → clams_python-1.7.4}/container/Containerfile +0 -0
  71. {clams_python-1.7.3 → clams_python-1.7.4}/container/ffmpeg-hf.containerfile +0 -0
  72. {clams_python-1.7.3 → clams_python-1.7.4}/container/ffmpeg-hf4.containerfile +0 -0
  73. {clams_python-1.7.3 → clams_python-1.7.4}/container/ffmpeg-hf5.containerfile +0 -0
  74. {clams_python-1.7.3 → clams_python-1.7.4}/container/ffmpeg-tf2.containerfile +0 -0
  75. {clams_python-1.7.3 → clams_python-1.7.4}/container/ffmpeg.containerfile +0 -0
  76. {clams_python-1.7.3 → clams_python-1.7.4}/container/generate_containers_yml.py +0 -0
  77. {clams_python-1.7.3 → clams_python-1.7.4}/container/hf.containerfile +0 -0
  78. {clams_python-1.7.3 → clams_python-1.7.4}/container/hf4.containerfile +0 -0
  79. {clams_python-1.7.3 → clams_python-1.7.4}/container/hf5.containerfile +0 -0
  80. {clams_python-1.7.3 → clams_python-1.7.4}/container/jdk8.containerfile +0 -0
  81. {clams_python-1.7.3 → clams_python-1.7.4}/container/opencv4-hf.containerfile +0 -0
  82. {clams_python-1.7.3 → clams_python-1.7.4}/container/opencv4-hf4.containerfile +0 -0
  83. {clams_python-1.7.3 → clams_python-1.7.4}/container/opencv4-hf5.containerfile +0 -0
  84. {clams_python-1.7.3 → clams_python-1.7.4}/container/opencv4-tf2.containerfile +0 -0
  85. {clams_python-1.7.3 → clams_python-1.7.4}/container/opencv4.containerfile +0 -0
  86. {clams_python-1.7.3 → clams_python-1.7.4}/container/tf2.containerfile +0 -0
  87. {clams_python-1.7.3 → clams_python-1.7.4}/documentation/appdirectory.rst +0 -0
  88. {clams_python-1.7.3 → clams_python-1.7.4}/documentation/appmetadata.rst +0 -0
  89. {clams_python-1.7.3 → clams_python-1.7.4}/documentation/autodoc/clams.app.rst +0 -0
  90. {clams_python-1.7.3 → clams_python-1.7.4}/documentation/autodoc/clams.appmetadata.rst +0 -0
  91. {clams_python-1.7.3 → clams_python-1.7.4}/documentation/autodoc/clams.backends.rst +0 -0
  92. {clams_python-1.7.3 → clams_python-1.7.4}/documentation/autodoc/clams.mmif_utils.rst +0 -0
  93. {clams_python-1.7.3 → clams_python-1.7.4}/documentation/autodoc/clams.restify.rst +0 -0
  94. {clams_python-1.7.3 → clams_python-1.7.4}/documentation/cli.rst +0 -0
  95. {clams_python-1.7.3 → clams_python-1.7.4}/documentation/conf.py +0 -0
  96. {clams_python-1.7.3 → clams_python-1.7.4}/documentation/gpu-apps.rst +0 -0
  97. {clams_python-1.7.3 → clams_python-1.7.4}/documentation/index.rst +0 -0
  98. {clams_python-1.7.3 → clams_python-1.7.4}/documentation/input-output.rst +0 -0
  99. {clams_python-1.7.3 → clams_python-1.7.4}/documentation/modules.rst +0 -0
  100. {clams_python-1.7.3 → clams_python-1.7.4}/documentation/tutorial.md +0 -0
  101. {clams_python-1.7.3 → clams_python-1.7.4}/pyproject.toml +0 -0
  102. {clams_python-1.7.3 → clams_python-1.7.4}/setup.cfg +0 -0
  103. {clams_python-1.7.3 → clams_python-1.7.4}/tests/metadata.py +0 -0
  104. {clams_python-1.7.3 → clams_python-1.7.4}/tests/pillow-logo.png +0 -0
  105. {clams_python-1.7.3 → clams_python-1.7.4}/tests/test_backends_hf.py +0 -0
  106. {clams_python-1.7.3 → clams_python-1.7.4}/tests/test_promptable.py +0 -0
@@ -1,4 +1,13 @@
1
1
 
2
+ ## releasing 1.7.3 (2026-07-09)
3
+ ### Overview
4
+ This patch includes critical bugfixes. See changes below.
5
+
6
+ ### Changes
7
+ * fixes handling of error during `annotate()` (and `_annotate()`) when running an app via cli.py (#297)
8
+ * added the `useReasoning` common parameter for promptable apps (#299) Note that release 1.7.2 was supposed to include this parameter (and the release note says so), but it was omitted by mistake.
9
+
10
+
2
11
  ## releasing 1.7.2 (2026-07-07)
3
12
  ### Overview
4
13
  This patch adds experimental handling of "reasoning traces" of propmtable apps
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: clams-python
3
- Version: 1.7.3
3
+ Version: 1.7.4
4
4
  Summary: A collection of APIs to develop CLAMS app for python
5
5
  License-Expression: Apache-2.0
6
6
  Project-URL: homepage, https://clams.ai
@@ -49,6 +49,10 @@ falsy_values = [
49
49
  ]
50
50
 
51
51
 
52
+ class EmptyOutputWarning(UserWarning):
53
+ """Raised when an app's ``_annotate()`` produces no non-empty views."""
54
+
55
+
52
56
  class ClamsApp(ABC):
53
57
  """
54
58
  An abstract class to define API's for ClamsApps. A CLAMS app should inherit
@@ -191,6 +195,15 @@ class ClamsApp(ABC):
191
195
  t = datetime.now()
192
196
  with warnings.catch_warnings(record=True) as ws:
193
197
  annotated, cuda_profiler = self._profile_cuda_memory(self._annotate)(mmif, **refined)
198
+ new_views = [v for v in annotated.views if v.id not in existing_view_ids]
199
+ is_empty = lambda v: len(v.annotations) == 0 and not v.has_error() and not v.has_warnings()
200
+ for v in new_views:
201
+ if is_empty(v):
202
+ del annotated.views._items[v.id] # no public API to remove a view (mmif-python)
203
+ if all(is_empty(v) for v in new_views):
204
+ warnings.warn(f'App "{self.metadata.identifier}" produced no annotations for this '
205
+ f'input; the input may lack expected content, or there may genuinely '
206
+ f'be nothing to annotate.', EmptyOutputWarning)
194
207
  if ws:
195
208
  issued_warnings.extend(ws)
196
209
  if issued_warnings:
@@ -301,35 +314,81 @@ class ClamsApp(ABC):
301
314
  """
302
315
  A method to "sign" a new view that this app creates at the beginning of annotation.
303
316
  Signing will populate the view metadata with information and configuration of this app.
304
- The parameters passed to the :meth:`~clams.app.ClamsApp._annotate` must be
305
- passed to this method. This means all parameters for "common" configuration that
306
- are consumed in :meth:`~clams.app.ClamsApp.annotate` should not be recorded in the
307
- view metadata.
317
+
318
+ The ``runtime_conf`` argument can be either "refined" or "raw" parameters,
319
+ and what ends up recorded in the view metadata differs accordingly:
320
+
321
+ * "refined" params: the output of :meth:`~clams.app.ClamsApp._refine_params`,
322
+ identifiable by the presence of the internal ``_RAW_PARAMS_KEY`` marker.
323
+ These carry both the user's raw input and the fully resolved configuration
324
+ (declared parameters with their defaults filled in). Signing then populates
325
+ **two** metadata fields: ``parameters`` with the raw user input (before
326
+ defaults), and ``appConfiguration`` with the resolved configuration,
327
+ including the universal parameters (e.g. ``pretty``, ``runningTime``,
328
+ ``hwFetch``, ``tfSamplingMode``).
329
+ * "raw" params: an unrefined ``Dict[str, List[str]]``. Only the
330
+ ``parameters`` field is populated; there is no resolved configuration
331
+ to record. See :meth:`~clams.app.ClamsApp.add_parameters_to_metadata`
332
+ for the input channels these values come from and how each is recorded.
333
+
334
+ These two cases exist because of two distinct execution paths. On the normal
335
+ annotation path, :meth:`~clams.app.ClamsApp.annotate` refines the parameters
336
+ before invoking :meth:`~clams.app.ClamsApp._annotate`, so a well-behaved app
337
+ forwards those already-refined params here and gets the full two-field record.
338
+ On the error path (:meth:`~clams.app.ClamsApp.set_error_view`), refinement may
339
+ be exactly what failed (:meth:`~clams.app.ClamsApp._refine_params` raises on a
340
+ missing required parameter or an invalid choice) so the error view is signed
341
+ with the raw params to guarantee the error is still recorded.
342
+
308
343
  :param view: a view to sign
309
- :param runtime_conf: runtime configuration of the app as k-v pairs
344
+ :param runtime_conf: runtime configuration of the app, either refined
345
+ (output of :meth:`~clams.app.ClamsApp._refine_params`) or raw k-v pairs
310
346
  """
311
347
  view.metadata.app = str(self.metadata.identifier)
312
348
  if self.metadata.app_tags:
313
349
  view.metadata.set_additional_property('appTags', list(self.metadata.app_tags))
314
- params_map = {p.name: p for p in self.metadata.parameters}
315
350
  if self._RAW_PARAMS_KEY in runtime_conf:
316
351
  for k, v in runtime_conf.items():
317
352
  if k == self._RAW_PARAMS_KEY:
318
- for orik, oriv in v.items():
319
- if orik in params_map and params_map[orik].multivalued:
320
- view.metadata.add_parameter(orik, str(oriv))
321
- else:
322
- view.metadata.add_parameter(orik, oriv[0])
353
+ self.add_parameters_to_metadata(view, v)
323
354
  else:
324
355
  view.metadata.add_app_configuration(k, v)
325
356
  else:
326
- # meaning the parameters directly from flask or argparser and values are in lists
327
- for k, v in runtime_conf.items():
328
- if k in params_map and params_map[k].multivalued:
329
- view.metadata.add_parameter(k, str(v))
330
- else:
331
- view.metadata.add_parameter(k, v[0])
332
-
357
+ # unrefined params passed directly (no _RAW_PARAMS_KEY marker), e.g.
358
+ # the error path; values are lists
359
+ self.add_parameters_to_metadata(view, runtime_conf)
360
+
361
+ def add_parameters_to_metadata(self, view: View, parameters: dict):
362
+ """
363
+ Record raw runtime parameters into the view's ``parameters`` metadata.
364
+
365
+ The ``parameters`` argument reaches an app through one of three channels:
366
+
367
+ #. a query string, when the app runs as an HTTP server (``app.py``, via
368
+ :class:`~clams.restify.Restifier`);
369
+ #. shell arguments, when the app runs as a CLI program (``cli.py``);
370
+ #. a JSON envelope built with the ``clams envelop`` command (see
371
+ :mod:`clams.envelop`), whose values are flattened by
372
+ :func:`~clams.envelop.normalize_params`.
373
+
374
+ In every case the values arrive as ``List[str]`` (the shape
375
+ :meth:`~clams.app.ClamsApp._refine_params` consumes). A single value is
376
+ recorded bare with the ``List`` wrapper stripped, multiple values as the
377
+ serialized list, and an empty list skipped as unset (only an envelope
378
+ such as ``{"x": []}`` produces one, guarding against an ``IndexError``
379
+ on ``v[0]``).
380
+
381
+ :param view: the view being signed
382
+ :param parameters: raw runtime parameters as ``Dict[str, List[str]]``
383
+ """
384
+ for k, v in parameters.items():
385
+ if len(v) == 0:
386
+ continue
387
+ elif len(v) == 1:
388
+ view.metadata.add_parameter(k, v[0])
389
+ else:
390
+ view.metadata.add_parameter(k, str(v))
391
+
333
392
  def set_error_view(self, mmif: Union[str, dict, Mmif], **runtime_conf: List[str]) -> Mmif:
334
393
  """
335
394
  A method to record an error instead of annotation results in the view
@@ -3,7 +3,7 @@ FROM ghcr.io/clamsproject/clams-python:$CLAMS_VERSION
3
3
  # See https://github.com/orgs/clamsproject/packages?tab=packages&q=clams-python for more base images
4
4
  # IF you want to automatically publish this image to the clamsproject organization,
5
5
  # 1. you should have generated this template without --no-github-actions flag
6
- # 1. to add arm64 support, change relevant line in .github/workflows/container.yml
6
+ # 1. to add arm64 support, set `arm64: true` in .github/workflows/publish.yml
7
7
  # * NOTE that a lots of software doesn't install/compile or run on arm64 architecture out of the box
8
8
  # * make sure you locally test the compatibility of all software dependencies before using arm64 support
9
9
  # 1. use a git tag to trigger the github action. You need to use git tag to properly set app version anyway
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: clams-python
3
- Version: 1.7.3
3
+ Version: 1.7.4
4
4
  Summary: A collection of APIs to develop CLAMS app for python
5
5
  License-Expression: Apache-2.0
6
6
  Project-URL: homepage, https://clams.ai
@@ -1,96 +1,96 @@
1
1
  {
2
2
  "files": [
3
- "README.md",
4
- "LICENSE",
5
- "pyproject.toml",
3
+ ".github/workflows/codecov.yml",
4
+ ".github/workflows/container.yml",
5
+ ".github/workflows/containers.yml",
6
+ ".github/workflows/issue-assign.yml",
7
+ ".github/workflows/issue-close.yml",
8
+ ".github/workflows/issue-infra-project.yml",
9
+ ".github/workflows/publish.yml",
10
+ ".github/workflows/release-check.yml",
11
+ ".gitignore",
6
12
  ".pytype.cfg",
7
13
  "CHANGELOG.md",
8
14
  "CONTRIBUTING.md",
9
- ".gitignore",
10
- "build-tools/publish.py",
11
- "build-tools/prep_release.py",
15
+ "LICENSE",
16
+ "README.md",
12
17
  "build-tools/build.py",
13
- "build-tools/docs.py",
14
18
  "build-tools/clean.py",
19
+ "build-tools/docs.py",
20
+ "build-tools/prep_release.py",
21
+ "build-tools/publish.py",
15
22
  "build-tools/test.py",
16
23
  "clams/__init__.py",
24
+ "clams/app/__init__.py",
25
+ "clams/appmetadata/__init__.py",
26
+ "clams/backends/__init__.py",
27
+ "clams/backends/hf.py",
17
28
  "clams/develop/__init__.py",
29
+ "clams/develop/templates/app/.dockerignore.template",
30
+ "clams/develop/templates/app/.gitignore.template",
31
+ "clams/develop/templates/app/Containerfile.template",
32
+ "clams/develop/templates/app/LICENSE.template",
33
+ "clams/develop/templates/app/README.md.template",
34
+ "clams/develop/templates/app/app.py.template",
35
+ "clams/develop/templates/app/cli.py.template",
36
+ "clams/develop/templates/app/metadata.py.template",
37
+ "clams/develop/templates/app/requirements.txt.template",
18
38
  "clams/develop/templates/gha/for-clams-team.md.template",
19
- "clams/develop/templates/gha/workflows/issue-assign.yml.template",
20
- "clams/develop/templates/gha/workflows/publish.yml.template",
21
39
  "clams/develop/templates/gha/workflows/issue-apps-project.yml.template",
40
+ "clams/develop/templates/gha/workflows/issue-assign.yml.template",
22
41
  "clams/develop/templates/gha/workflows/issue-close.yml.template",
42
+ "clams/develop/templates/gha/workflows/publish.yml.template",
23
43
  "clams/develop/templates/utl-tf/__init__.py.template",
24
44
  "clams/develop/templates/utl-tf/timeframe.py.template",
25
- "clams/develop/templates/app/Containerfile.template",
26
- "clams/develop/templates/app/metadata.py.template",
27
- "clams/develop/templates/app/README.md.template",
28
- "clams/develop/templates/app/.dockerignore.template",
29
- "clams/develop/templates/app/requirements.txt.template",
30
- "clams/develop/templates/app/LICENSE.template",
31
- "clams/develop/templates/app/.gitignore.template",
32
- "clams/develop/templates/app/app.py.template",
33
- "clams/develop/templates/app/cli.py.template",
34
- "clams/restify/__init__.py",
35
45
  "clams/envelop/__init__.py",
36
- "clams/appmetadata/__init__.py",
37
- "clams/app/__init__.py",
46
+ "clams/restify/__init__.py",
38
47
  "clams/serve/__init__.py",
39
48
  "clams/ver/__init__.py",
40
- "clams/backends/__init__.py",
41
- "clams/backends/hf.py",
42
- "documentation/modules.rst",
43
- "documentation/target-versions.csv",
49
+ "container/Containerfile",
50
+ "container/ffmpeg-hf.containerfile",
51
+ "container/ffmpeg-hf4.containerfile",
52
+ "container/ffmpeg-hf5.containerfile",
53
+ "container/ffmpeg-tf2.containerfile",
54
+ "container/ffmpeg-torch2.containerfile",
55
+ "container/ffmpeg.containerfile",
56
+ "container/generate_containers_yml.py",
57
+ "container/hf.containerfile",
58
+ "container/hf4.containerfile",
59
+ "container/hf5.containerfile",
60
+ "container/jdk8.containerfile",
61
+ "container/opencv4-hf.containerfile",
62
+ "container/opencv4-hf4.containerfile",
63
+ "container/opencv4-hf5.containerfile",
64
+ "container/opencv4-tf2.containerfile",
65
+ "container/opencv4-torch2.containerfile",
66
+ "container/opencv4.containerfile",
67
+ "container/tf2.containerfile",
68
+ "container/torch2.containerfile",
44
69
  "documentation/app-baseclasses.rst",
45
- "documentation/gpu-apps.rst",
46
- "documentation/clamsapp.rst",
47
70
  "documentation/appdirectory.rst",
48
- "documentation/index.rst",
49
- "documentation/tutorial.md",
50
- "documentation/introduction.rst",
51
71
  "documentation/appmetadata.rst",
52
- "documentation/cli.rst",
53
- "documentation/runtime-params.rst",
54
- "documentation/input-output.rst",
55
- "documentation/conf.py",
56
- "documentation/autodoc/clams.restify.rst",
57
72
  "documentation/autodoc/clams.app.rst",
73
+ "documentation/autodoc/clams.appmetadata.rst",
58
74
  "documentation/autodoc/clams.backends.rst",
59
75
  "documentation/autodoc/clams.mmif_utils.rst",
60
- "documentation/autodoc/clams.appmetadata.rst",
61
- "tests/test_promptable.py",
76
+ "documentation/autodoc/clams.restify.rst",
77
+ "documentation/clamsapp.rst",
78
+ "documentation/cli.rst",
79
+ "documentation/conf.py",
80
+ "documentation/gpu-apps.rst",
81
+ "documentation/index.rst",
82
+ "documentation/input-output.rst",
83
+ "documentation/introduction.rst",
84
+ "documentation/modules.rst",
85
+ "documentation/runtime-params.rst",
86
+ "documentation/target-versions.csv",
87
+ "documentation/tutorial.md",
88
+ "pyproject.toml",
89
+ "tests/metadata.py",
62
90
  "tests/pillow-logo.png",
63
91
  "tests/test_backends_hf.py",
64
- "tests/test_envelope.py",
65
- "tests/metadata.py",
66
92
  "tests/test_clamsapp.py",
67
- "container/tf2.containerfile",
68
- "container/opencv4-hf.containerfile",
69
- "container/torch2.containerfile",
70
- "container/opencv4-hf4.containerfile",
71
- "container/Containerfile",
72
- "container/hf4.containerfile",
73
- "container/ffmpeg-hf.containerfile",
74
- "container/ffmpeg-tf2.containerfile",
75
- "container/generate_containers_yml.py",
76
- "container/opencv4-tf2.containerfile",
77
- "container/opencv4-torch2.containerfile",
78
- "container/hf.containerfile",
79
- "container/jdk8.containerfile",
80
- "container/hf5.containerfile",
81
- "container/ffmpeg.containerfile",
82
- "container/ffmpeg-hf5.containerfile",
83
- "container/ffmpeg-hf4.containerfile",
84
- "container/opencv4-hf5.containerfile",
85
- "container/ffmpeg-torch2.containerfile",
86
- "container/opencv4.containerfile",
87
- ".github/workflows/container.yml",
88
- ".github/workflows/containers.yml",
89
- ".github/workflows/issue-close.yml",
90
- ".github/workflows/codecov.yml",
91
- ".github/workflows/release-check.yml",
92
- ".github/workflows/issue-infra-project.yml",
93
- ".github/workflows/issue-assign.yml",
94
- ".github/workflows/publish.yml"
93
+ "tests/test_envelope.py",
94
+ "tests/test_promptable.py"
95
95
  ]
96
96
  }
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "1.7.4",
3
+ "distance": 0,
4
+ "node": "g4a9316fc63b59287b43542fd15047a9a28028510",
5
+ "dirty": false,
6
+ "branch": "HEAD",
7
+ "node_date": "2026-09-01"
8
+ }
@@ -0,0 +1,13 @@
1
+ ARG clams_version
2
+ FROM ghcr.io/clamsproject/clams-python-ffmpeg:$clams_version
3
+ LABEL org.opencontainers.image.description="clams-python-ffmpeg-torch2 image is shipped with clams-python, ffmpeg, and PyTorch2"
4
+
5
+ RUN pip install --no-cache-dir torch==2.*
6
+
7
+ # PyTorch ships Triton, which JIT-compiles a small host-side CUDA driver shim
8
+ # at runtime -- that needs a C compiler + libc headers. Keep it minimal
9
+ # (gcc + libc6-dev); build-essential is not required.
10
+ RUN apt-get update \
11
+ && apt-get install -y --no-install-recommends gcc libc6-dev \
12
+ && rm -rf /var/lib/apt/lists/*
13
+
@@ -0,0 +1,13 @@
1
+ ARG clams_version
2
+ FROM ghcr.io/clamsproject/clams-python-opencv4:$clams_version
3
+ LABEL org.opencontainers.image.description="clams-python-opencv4-torch2 image is shipped with clams-python, opencv4 (ffmpeg backend), and PyTorch2"
4
+
5
+ RUN pip install --no-cache-dir torch==2.*
6
+
7
+ # PyTorch ships Triton, which JIT-compiles a small host-side CUDA driver shim
8
+ # at runtime -- that needs a C compiler + libc headers. Keep it minimal
9
+ # (gcc + libc6-dev); build-essential is not required.
10
+ RUN apt-get update \
11
+ && apt-get install -y --no-install-recommends gcc libc6-dev \
12
+ && rm -rf /var/lib/apt/lists/*
13
+
@@ -0,0 +1,13 @@
1
+ ARG clams_version
2
+ FROM ghcr.io/clamsproject/clams-python:$clams_version
3
+ LABEL org.opencontainers.image.description="clams-python-torch2 image is shipped with clams-python and PyTorch2"
4
+
5
+ RUN pip install --no-cache-dir torch==2.*
6
+
7
+ # PyTorch ships Triton, which JIT-compiles a small host-side CUDA driver shim
8
+ # at runtime -- that needs a C compiler + libc headers. Keep it minimal
9
+ # (gcc + libc6-dev); build-essential is not required.
10
+ RUN apt-get update \
11
+ && apt-get install -y --no-install-recommends gcc libc6-dev \
12
+ && rm -rf /var/lib/apt/lists/*
13
+
@@ -550,6 +550,11 @@ A consumer of the output MMIF can read the resolved revision directly
550
550
  from the view metadata, with no cross-reference to the app metadata
551
551
  required.
552
552
 
553
+ This ``model`` handling is a specific instance of the general MMIF convention
554
+ for recording runtime configuration; see the view-metadata section of the
555
+ `MMIF specification <https://mmif.clams.ai/1.2.0/>`_ for the definitions of the
556
+ ``parameters`` and ``appConfiguration`` fields.
557
+
553
558
  What the base class provides
554
559
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
555
560
 
@@ -299,6 +299,18 @@ The envelope looks like this:
299
299
  The output is always raw MMIF, regardless of input format, so downstream
300
300
  pipeline steps are unaffected.
301
301
 
302
+ .. note::
303
+
304
+ The values in the envelope's ``parameters`` block (not to be confused with
305
+ ``view.metadata.parameters``, the recorded output field) are interpreted as
306
+ native JSON. Use real numbers, booleans, arrays, and objects (e.g. ``0.7``, ``true``,
307
+ ``["a", "b"]``, ``{"B": "bars"}``), not the URL-encoded string form. To
308
+ reproduce a past run from an existing MMIF, copy the target view's
309
+ ``appConfiguration`` into the envelope's ``parameters``; it holds the fully
310
+ resolved, native-JSON configuration. Copying from ``view.metadata.parameters``
311
+ is not reliable, since that field is the raw "as typed" transcript and is not
312
+ guaranteed to round-trip.
313
+
302
314
  You can still combine the envelope with query string parameters.
303
315
  When the same parameter appears in both, the **query string takes priority**,
304
316
  which allows quick overrides without editing the parameter file:
@@ -69,7 +69,7 @@ When you inherit :class:`~clams.app.ClamsApp`, you need to implement
69
69
  As a developer you can expose different behaviors of the ``annotate()`` method by providing configurable parameters as keyword arguments of the method. For example, you can have the user specify a re-sample rate of an audio file to be analyzed by providing a ``resample_rate`` parameter.
70
70
 
71
71
  .. note::
72
- These runtime configurations are not part of the MMIF input, but for reproducible analysis, you should record these configurations in the output MMIF.
72
+ These runtime configurations are not part of the MMIF input. The SDK records them automatically in the output view metadata for reproducibility: the raw user input in ``view.metadata.parameters`` and the fully resolved configuration in ``view.metadata.appConfiguration``. Both fields are defined in the view-metadata section of the `MMIF specification <https://mmif.clams.ai/1.2.0/>`_.
73
73
 
74
74
  .. note::
75
75
  Some runtime parameters are managed by the SDK itself rather than declared per-app. The *universal* parameters in :const:`clams.app.ClamsApp.universal_parameters` are one such set; they are auto-added to every CLAMS app. Specialized base classes (see below) add their own SDK-managed parameter sets on top.
@@ -32,9 +32,17 @@ annotations. See an example below.
32
32
 
33
33
  ...
34
34
 
35
- When you use a configuration parameter in your app, you should also expose it
35
+ When you use a configuration parameter in your app, you should also expose it
36
36
  to the user via the app metadata. See :ref:`appmetadata` section for more details.
37
37
 
38
+ .. note::
39
+ Whichever channel supplies them, the SDK records the runtime parameters in the
40
+ output view metadata: the raw user input (as strings) in
41
+ ``view.metadata.parameters``, and the fully refined configuration (defaults filled
42
+ and values cast) in ``view.metadata.appConfiguration``. See the view-metadata
43
+ section of the `MMIF specification <https://mmif.clams.ai/1.2.0/>`_ for the
44
+ authoritative definitions of these two fields.
45
+
38
46
  As HTTP Server
39
47
  --------------
40
48
 
@@ -1,4 +1,5 @@
1
1
  "``clams-python`` version","``mmif-python`` version","Target MMIF Specification"
2
+ `1.7.4 <https://pypi.org/project/clams-python/1.7.4/>`__,`1.5.3 <https://pypi.org/project/mmif-python/1.5.3/>`__,`1.2.0 <https://mmif.clams.ai/1.2.0/>`__
2
3
  `1.7.3 <https://pypi.org/project/clams-python/1.7.3/>`__,`1.5.1 <https://pypi.org/project/mmif-python/1.5.1/>`__,`1.2.0 <https://mmif.clams.ai/1.2.0/>`__
3
4
  `1.7.2 <https://pypi.org/project/clams-python/1.7.2/>`__,`1.5.1 <https://pypi.org/project/mmif-python/1.5.1/>`__,`1.2.0 <https://mmif.clams.ai/1.2.0/>`__
4
5
  `1.7.1 <https://pypi.org/project/clams-python/1.7.1/>`__,`1.5.1 <https://pypi.org/project/mmif-python/1.5.1/>`__,`1.2.0 <https://mmif.clams.ai/1.2.0/>`__
@@ -262,6 +262,34 @@ class TestClamsApp(unittest.TestCase):
262
262
  self.assertEqual(len(v4.metadata.appConfiguration), 6)
263
263
  self.assertEqual(len(v4.metadata.parameters['multivalued_param']), len(str(multiple_values)))
264
264
 
265
+ def test_add_parameters_to_metadata_len_branch(self):
266
+ # recording branches purely on the number of values, uniformly for
267
+ # declared and undeclared params (issue #296)
268
+ m = Mmif(self.in_mmif)
269
+
270
+ # single value -> bare value, no list brackets
271
+ v = m.new_view()
272
+ self.app.add_parameters_to_metadata(v, {'undeclared': ['solo']})
273
+ self.assertEqual(v.metadata.parameters['undeclared'], 'solo')
274
+
275
+ # multiple values -> serialized list, no data loss
276
+ v = m.new_view()
277
+ self.app.add_parameters_to_metadata(v, {'undeclared_multi': ['a', 'b']})
278
+ self.assertEqual(v.metadata.parameters['undeclared_multi'], str(['a', 'b']))
279
+
280
+ # empty list (only an envelope like {"x": []}/{"x": {}} can produce it)
281
+ # -> treated as unset, skipped, and must not raise IndexError
282
+ v = m.new_view()
283
+ self.app.add_parameters_to_metadata(v, {'empty': []})
284
+ self.assertNotIn('empty', v.metadata.parameters)
285
+
286
+ # a declared multivalued param given a single value is also recorded clean
287
+ self.app.metadata.add_parameter(
288
+ 'mv', 'multivalued', type='string', multivalued=True, default=[])
289
+ v = m.new_view()
290
+ self.app.add_parameters_to_metadata(v, {'mv': ['only']})
291
+ self.assertEqual(v.metadata.parameters['mv'], 'only')
292
+
265
293
  def test_app_tags_default_empty(self):
266
294
  # apps that don't declare tags should not write an appTags field to view metadata
267
295
  self.assertEqual(self.app.metadata.app_tags, [])
@@ -417,6 +445,55 @@ class TestClamsApp(unittest.TestCase):
417
445
  self.assertEqual(len(last_view.metadata.contains), 0)
418
446
  self.assertEqual(len(last_view.metadata.error), 2)
419
447
 
448
+ def test_empty_view_becomes_warning_view(self):
449
+ class EmptyClamsApp(clams.app.ClamsApp):
450
+ def _appmetadata(self):
451
+ pass
452
+ def _annotate(self, mmif, **kwargs):
453
+ new_view = mmif.new_view()
454
+ self.sign_view(new_view, kwargs)
455
+ new_view.new_contain(AnnotationTypes.TimeFrame)
456
+ return mmif
457
+ out_mmif = Mmif(EmptyClamsApp().annotate(self.in_mmif))
458
+ self.assertEqual(len(out_mmif.views), 1)
459
+ view = next(iter(out_mmif.views))
460
+ self.assertEqual(len(view.annotations), 0)
461
+ self.assertTrue(any('EmptyOutputWarning' in w for w in view.metadata.warnings))
462
+
463
+ def test_partial_empty_views_are_pruned_without_warning(self):
464
+ class MixedClamsApp(clams.app.ClamsApp):
465
+ def _appmetadata(self):
466
+ pass
467
+ def _annotate(self, mmif, **kwargs):
468
+ populated = mmif.new_view()
469
+ self.sign_view(populated, kwargs)
470
+ populated.new_contain(AnnotationTypes.TimeFrame)
471
+ populated.new_annotation(AnnotationTypes.TimeFrame, start=0, end=1)
472
+ empty = mmif.new_view()
473
+ self.sign_view(empty, kwargs)
474
+ empty.new_contain(AnnotationTypes.TimeFrame)
475
+ return mmif
476
+ out_mmif = Mmif(MixedClamsApp().annotate(self.in_mmif))
477
+ self.assertEqual(len(out_mmif.views), 1)
478
+ view = next(iter(out_mmif.views))
479
+ self.assertEqual(len(view.annotations), 1)
480
+ self.assertFalse(view.has_warnings())
481
+
482
+ def test_self_reported_error_view_is_not_pruned(self):
483
+ class ErroringClamsApp(clams.app.ClamsApp):
484
+ def _appmetadata(self):
485
+ pass
486
+ def _annotate(self, mmif, **kwargs):
487
+ new_view = mmif.new_view()
488
+ self.sign_view(new_view, kwargs)
489
+ new_view.set_error('simulated failure', 'trace')
490
+ return mmif
491
+ out_mmif = Mmif(ErroringClamsApp().annotate(self.in_mmif))
492
+ self.assertEqual(len(out_mmif.views), 1)
493
+ view = next(iter(out_mmif.views))
494
+ self.assertTrue(view.has_error())
495
+ self.assertFalse(view.has_warnings())
496
+
420
497
  def test_gpu_mem_fields_default_zero(self):
421
498
  """GPU memory fields default to 0."""
422
499
  metadata = AppMetadata(
@@ -236,6 +236,19 @@ class TestRestifierEnvelope(unittest.TestCase):
236
236
  self.assertEqual(res.status_code, 200)
237
237
  Mmif(res.get_data(as_text=True))
238
238
 
239
+ def test_empty_collection_param_does_not_crash(self):
240
+ # {"x": []} / {"x": {}} normalize to an empty list; recording must skip
241
+ # the param instead of raising IndexError on v[0] (issue #296)
242
+ for empty in ([], {}):
243
+ envelope_str = create_envelope(
244
+ self.mmif_str, {'emptyparam': empty}
245
+ )
246
+ res = self.client.post('/', data=envelope_str)
247
+ self.assertEqual(res.status_code, 200)
248
+ out = Mmif(res.get_data(as_text=True))
249
+ for view in out.views:
250
+ self.assertNotIn('emptyparam', view.metadata.parameters)
251
+
239
252
  PREFIX = "Invalid input data. See below for validation error."
240
253
 
241
254
  def test_envelope_missing_mmif(self):
@@ -1,8 +0,0 @@
1
- {
2
- "tag": "1.7.3",
3
- "distance": 0,
4
- "node": "gea1bca511e70720b1f46476fbb5f41ff2d9329cd",
5
- "dirty": false,
6
- "branch": "HEAD",
7
- "node_date": "2026-07-09"
8
- }
@@ -1,6 +0,0 @@
1
- ARG clams_version
2
- FROM ghcr.io/clamsproject/clams-python-ffmpeg:$clams_version
3
- LABEL org.opencontainers.image.description="clams-python-ffmpeg-torch2 image is shipped with clams-python, ffmpeg, and PyTorch2"
4
-
5
- RUN pip install --no-cache-dir torch==2.*
6
-
@@ -1,6 +0,0 @@
1
- ARG clams_version
2
- FROM ghcr.io/clamsproject/clams-python-opencv4:$clams_version
3
- LABEL org.opencontainers.image.description="clams-python-opencv4-torch2 image is shipped with clams-python, opencv4 (ffmpeg backend), and PyTorch2"
4
-
5
- RUN pip install --no-cache-dir torch==2.*
6
-
@@ -1,6 +0,0 @@
1
- ARG clams_version
2
- FROM ghcr.io/clamsproject/clams-python:$clams_version
3
- LABEL org.opencontainers.image.description="clams-python-torch2 image is shipped with clams-python and PyTorch2"
4
-
5
- RUN pip install --no-cache-dir torch==2.*
6
-
File without changes
File without changes
File without changes
File without changes
File without changes