clams-python 1.7.1__tar.gz → 1.7.2__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.
- {clams_python-1.7.1 → clams_python-1.7.2}/CHANGELOG.md +5 -0
- {clams_python-1.7.1/clams_python.egg-info → clams_python-1.7.2}/PKG-INFO +1 -1
- {clams_python-1.7.1 → clams_python-1.7.2}/clams/app/__init__.py +134 -15
- {clams_python-1.7.1 → clams_python-1.7.2/clams_python.egg-info}/PKG-INFO +1 -1
- {clams_python-1.7.1 → clams_python-1.7.2}/clams_python.egg-info/SOURCES.txt +3 -0
- clams_python-1.7.2/clams_python.egg-info/scm_file_list.json +96 -0
- clams_python-1.7.2/clams_python.egg-info/scm_version.json +8 -0
- clams_python-1.7.2/documentation/autodoc/clams.backends.rst +15 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/documentation/target-versions.csv +1 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/tests/test_promptable.py +134 -14
- {clams_python-1.7.1 → clams_python-1.7.2}/.github/workflows/codecov.yml +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/.github/workflows/container.yml +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/.github/workflows/containers.yml +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/.github/workflows/issue-assign.yml +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/.github/workflows/issue-close.yml +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/.github/workflows/issue-infra-project.yml +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/.github/workflows/publish.yml +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/.github/workflows/release-check.yml +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/.gitignore +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/.pytype.cfg +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/CONTRIBUTING.md +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/LICENSE +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/README.md +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/build-tools/build.py +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/build-tools/clean.py +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/build-tools/docs.py +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/build-tools/prep_release.py +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/build-tools/publish.py +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/build-tools/test.py +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/clams/__init__.py +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/clams/appmetadata/__init__.py +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/clams/backends/__init__.py +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/clams/backends/hf.py +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/clams/develop/__init__.py +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/clams/develop/templates/app/.dockerignore.template +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/clams/develop/templates/app/.gitignore.template +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/clams/develop/templates/app/Containerfile.template +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/clams/develop/templates/app/LICENSE.template +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/clams/develop/templates/app/README.md.template +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/clams/develop/templates/app/app.py.template +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/clams/develop/templates/app/cli.py.template +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/clams/develop/templates/app/metadata.py.template +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/clams/develop/templates/app/requirements.txt.template +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/clams/develop/templates/gha/for-clams-team.md.template +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/clams/develop/templates/gha/workflows/issue-apps-project.yml.template +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/clams/develop/templates/gha/workflows/issue-assign.yml.template +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/clams/develop/templates/gha/workflows/issue-close.yml.template +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/clams/develop/templates/gha/workflows/publish.yml.template +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/clams/develop/templates/utl-tf/__init__.py.template +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/clams/develop/templates/utl-tf/timeframe.py.template +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/clams/envelop/__init__.py +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/clams/restify/__init__.py +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/clams/serve/__init__.py +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/clams/ver/__init__.py +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/clams_python.egg-info/dependency_links.txt +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/clams_python.egg-info/entry_points.txt +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/clams_python.egg-info/requires.txt +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/clams_python.egg-info/top_level.txt +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/container/Containerfile +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/container/ffmpeg-hf.containerfile +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/container/ffmpeg-hf4.containerfile +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/container/ffmpeg-hf5.containerfile +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/container/ffmpeg-tf2.containerfile +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/container/ffmpeg-torch2.containerfile +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/container/ffmpeg.containerfile +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/container/generate_containers_yml.py +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/container/hf.containerfile +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/container/hf4.containerfile +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/container/hf5.containerfile +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/container/jdk8.containerfile +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/container/opencv4-hf.containerfile +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/container/opencv4-hf4.containerfile +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/container/opencv4-hf5.containerfile +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/container/opencv4-tf2.containerfile +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/container/opencv4-torch2.containerfile +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/container/opencv4.containerfile +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/container/tf2.containerfile +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/container/torch2.containerfile +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/documentation/app-baseclasses.rst +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/documentation/appdirectory.rst +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/documentation/appmetadata.rst +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/documentation/autodoc/clams.app.rst +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/documentation/autodoc/clams.appmetadata.rst +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/documentation/autodoc/clams.mmif_utils.rst +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/documentation/autodoc/clams.restify.rst +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/documentation/clamsapp.rst +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/documentation/cli.rst +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/documentation/conf.py +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/documentation/gpu-apps.rst +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/documentation/index.rst +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/documentation/input-output.rst +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/documentation/introduction.rst +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/documentation/modules.rst +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/documentation/runtime-params.rst +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/documentation/tutorial.md +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/pyproject.toml +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/setup.cfg +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/tests/metadata.py +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/tests/pillow-logo.png +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/tests/test_backends_hf.py +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/tests/test_clamsapp.py +0 -0
- {clams_python-1.7.1 → clams_python-1.7.2}/tests/test_envelope.py +0 -0
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
|
|
2
|
+
## releasing 1.7.1 (2026-06-19)
|
|
3
|
+
### Overview
|
|
4
|
+
Updates the `mmif-python` SDK dependency to 1.5.1 to incorporate a bugfix in VDH, with some other minor documentation generation improvements.
|
|
5
|
+
|
|
6
|
+
|
|
2
7
|
## releasing 1.7.0 (2026-05-29)
|
|
3
8
|
### Overview
|
|
4
9
|
This release introduces a specialized base class `ClamsPromptableApp` and its HuggingFace specialization `ClamsHFPromptableApp` for CLAMS apps that wrap LLM / VLM / ALM / LMM-based analyzers, along with other HF-related helper code in various places. Additionally, it adds `appTags` field for app categorization in app metadata, and new timeframe related local utility recipe for `clams develop --recipe utl-tf` scaffold.
|
|
@@ -1005,8 +1005,10 @@ class ClamsPromptableApp(ClamsApp):
|
|
|
1005
1005
|
"""
|
|
1006
1006
|
Persist a single LLM text response into a view. Writes one
|
|
1007
1007
|
``TextDocument`` (containing the response) plus possible
|
|
1008
|
-
grounding via an ``Alignment`` annotation and ``origins`` /
|
|
1009
|
-
``origination`` properties on the TD.
|
|
1008
|
+
grounding via an ``Alignment`` annotation and ``origins`` /
|
|
1009
|
+
``origination`` properties on the TD. The TD also inherits the
|
|
1010
|
+
``document`` of its ``source`` annotation, so it records which
|
|
1011
|
+
medium the response describes.
|
|
1010
1012
|
|
|
1011
1013
|
The two grounding link kinds are semantically distinct:
|
|
1012
1014
|
|
|
@@ -1028,7 +1030,12 @@ class ClamsPromptableApp(ClamsApp):
|
|
|
1028
1030
|
``Alignment`` first if needed.
|
|
1029
1031
|
:param source: ``id`` of the annotation to record as the
|
|
1030
1032
|
cross-modal anchor of the response (see above).
|
|
1031
|
-
:param response: the text
|
|
1033
|
+
:param response: the text to store as the TextDocument's
|
|
1034
|
+
value. Typically the model's generated answer, but the
|
|
1035
|
+
caller MAY pass a post-processed form (e.g. with a
|
|
1036
|
+
reasoning block stripped, whitespace trimmed, or otherwise
|
|
1037
|
+
reformatted); this method stores whatever it is given,
|
|
1038
|
+
verbatim, as the ``text``.
|
|
1032
1039
|
:param origins: optional list of ``id``\\s of annotations the
|
|
1033
1040
|
response was *derived* from. Must be paired with
|
|
1034
1041
|
``origination``.
|
|
@@ -1039,15 +1046,24 @@ class ClamsPromptableApp(ClamsApp):
|
|
|
1039
1046
|
``origins``.
|
|
1040
1047
|
:param reasoning_trace: optional model-side reasoning trace
|
|
1041
1048
|
(a chain-of-thought / scratchpad string, NOT a Python
|
|
1042
|
-
traceback).
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1049
|
+
traceback). When non-``None``, it is stored on the
|
|
1050
|
+
returned TextDocument as the ``modelReasoningTrace``
|
|
1051
|
+
property, kept OUT of the ``text`` (which holds whatever
|
|
1052
|
+
``response`` the caller passed). The point is separation,
|
|
1053
|
+
not any claim about the ``text`` being verbatim: storing a
|
|
1054
|
+
trace adds no annotation and no new type, so the type-level
|
|
1055
|
+
output shape is identical whether or not a trace is stored.
|
|
1056
|
+
Pass ``None`` (the default) to store no trace. The property
|
|
1057
|
+
name is provisional pending the
|
|
1058
|
+
vocabulary convention (clamsproject/clams-vocabulary#11).
|
|
1046
1059
|
:return: ``(TextDocument, Alignment)`` tuple of the new
|
|
1047
1060
|
annotations.
|
|
1048
1061
|
:raises ValueError: if exactly one of ``origins`` /
|
|
1049
|
-
``origination`` is set
|
|
1050
|
-
or
|
|
1062
|
+
``origination`` is set (they must be supplied together or
|
|
1063
|
+
both omitted), or if the ``source`` annotation carries no
|
|
1064
|
+
``document`` to ground the response to (a malformed input).
|
|
1065
|
+
:raises KeyError: if ``source`` does not resolve to an
|
|
1066
|
+
annotation in the MMIF.
|
|
1051
1067
|
"""
|
|
1052
1068
|
if bool(origins) != bool(origination):
|
|
1053
1069
|
raise ValueError(
|
|
@@ -1056,6 +1072,20 @@ class ClamsPromptableApp(ClamsApp):
|
|
|
1056
1072
|
f"origins={origins!r}, origination={origination!r}."
|
|
1057
1073
|
)
|
|
1058
1074
|
td = view.new_textdocument(text=response)
|
|
1075
|
+
# Ground the TextDocument to the medium it describes: copy `document`
|
|
1076
|
+
# from the source annotation (e.g. the parent TimeFrame's video) onto
|
|
1077
|
+
# the TD, so it records which document the response pertains to. A
|
|
1078
|
+
# grounded response must be traceable to a document, so a source that
|
|
1079
|
+
# carries no `document` means the upstream MMIF is malformed -- raise
|
|
1080
|
+
# rather than silently drop the grounding.
|
|
1081
|
+
source_ann = view._parent_mmif[source]
|
|
1082
|
+
source_doc = source_ann.get_property('document')
|
|
1083
|
+
if not source_doc:
|
|
1084
|
+
raise ValueError(
|
|
1085
|
+
f"source annotation {source!r} has no `document` property to "
|
|
1086
|
+
f"ground the response to; the input MMIF is malformed."
|
|
1087
|
+
)
|
|
1088
|
+
td.add_property('document', source_doc)
|
|
1059
1089
|
if origins:
|
|
1060
1090
|
td.add_property('origins', origins)
|
|
1061
1091
|
td.add_property('origination', origination)
|
|
@@ -1065,13 +1095,52 @@ class ClamsPromptableApp(ClamsApp):
|
|
|
1065
1095
|
target=td.id,
|
|
1066
1096
|
)
|
|
1067
1097
|
if reasoning_trace is not None:
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1098
|
+
# Store the trace as a property on the TD rather than a separate
|
|
1099
|
+
# document: keeps the type-level output shape invariant (same
|
|
1100
|
+
# annotation, same `contains`, same graph) whether or not a trace is
|
|
1101
|
+
# present -- a strict enrichment, independent of whatever `response`
|
|
1102
|
+
# the caller put in `text`. Property name provisional pending
|
|
1103
|
+
# clamsproject/clams-vocabulary#11.
|
|
1104
|
+
td.add_property('modelReasoningTrace', reasoning_trace)
|
|
1072
1105
|
return td, align
|
|
1073
1106
|
|
|
1074
|
-
|
|
1107
|
+
@staticmethod
|
|
1108
|
+
def split_tagged_reasoning_trace(
|
|
1109
|
+
text: str,
|
|
1110
|
+
open_tag: str = '<think>',
|
|
1111
|
+
close_tag: str = '</think>',
|
|
1112
|
+
) -> Tuple[str, Optional[str]]:
|
|
1113
|
+
"""
|
|
1114
|
+
Split a reasoning model's output into ``(answer, trace)`` for the
|
|
1115
|
+
inline XML-tag style: a single reasoning block delimited by
|
|
1116
|
+
``open_tag`` / ``close_tag`` (default ``<think>`` / ``</think>``)
|
|
1117
|
+
embedded in the decoded text, e.g. as emitted by DeepSeek-R1 or
|
|
1118
|
+
Qwen3.
|
|
1119
|
+
|
|
1120
|
+
It does NOT handle models that isolate reasoning in a separate
|
|
1121
|
+
token-delimited channel rather than an inline tag block (e.g.
|
|
1122
|
+
gpt-oss, Gemma 4); those need their own parsing in the app. Trace
|
|
1123
|
+
handling is the app's responsibility either way -- call this when
|
|
1124
|
+
the model fits the inline-tag style, or parse it yourself.
|
|
1125
|
+
|
|
1126
|
+
``answer`` is everything after the final ``close_tag`` (stripped);
|
|
1127
|
+
``trace`` is the text between the tags, or ``None`` when no closed
|
|
1128
|
+
block is present -- so it is safe to call on non-reasoning output,
|
|
1129
|
+
which returns ``(text.strip(), None)``.
|
|
1130
|
+
|
|
1131
|
+
:param text: raw decoded model output.
|
|
1132
|
+
:param open_tag: opening marker of the inline reasoning block.
|
|
1133
|
+
:param close_tag: closing marker of the inline reasoning block.
|
|
1134
|
+
:return: ``(answer, trace_or_None)``.
|
|
1135
|
+
"""
|
|
1136
|
+
ci = text.rfind(close_tag)
|
|
1137
|
+
if ci == -1:
|
|
1138
|
+
return text.strip(), None
|
|
1139
|
+
answer = text[ci + len(close_tag):].strip()
|
|
1140
|
+
oi = text.find(open_tag)
|
|
1141
|
+
trace = (text[oi + len(open_tag):ci].strip()
|
|
1142
|
+
if oi != -1 and oi < ci else None)
|
|
1143
|
+
return answer, trace
|
|
1075
1144
|
class ClamsHFPromptableApp(ClamsPromptableApp):
|
|
1076
1145
|
"""
|
|
1077
1146
|
Base class for promptable CLAMS apps backed by a local
|
|
@@ -1289,7 +1358,7 @@ class ClamsHFPromptableApp(ClamsPromptableApp):
|
|
|
1289
1358
|
dtype=self.DTYPE,
|
|
1290
1359
|
padding_side=self.PADDING_SIDE,
|
|
1291
1360
|
revision=revision,
|
|
1292
|
-
model_kwargs=self.
|
|
1361
|
+
model_kwargs=self.model_load_kwargs(model_id, revision),
|
|
1293
1362
|
processor_kwargs=self.PROCESSOR_KWARGS,
|
|
1294
1363
|
)
|
|
1295
1364
|
self.logger.info(f"HF model loaded on {triple[2]}")
|
|
@@ -1297,6 +1366,32 @@ class ClamsHFPromptableApp(ClamsPromptableApp):
|
|
|
1297
1366
|
self.processor, self.model, self.device = triple
|
|
1298
1367
|
return triple
|
|
1299
1368
|
|
|
1369
|
+
def model_load_kwargs(self, model_id: str, revision: str) -> dict:
|
|
1370
|
+
"""
|
|
1371
|
+
The ``model_kwargs`` forwarded to
|
|
1372
|
+
:func:`~clams.backends.hf.load_hf_model` (and on to
|
|
1373
|
+
``from_pretrained``) for a SPECIFIC family member. The base
|
|
1374
|
+
implementation returns a copy of the class-level
|
|
1375
|
+
:py:attr:`MODEL_KWARGS`, applied uniformly to every variant.
|
|
1376
|
+
|
|
1377
|
+
Override when a family mixes per-variant load requirements that
|
|
1378
|
+
a single class-level ``MODEL_KWARGS`` cannot express -- e.g. a
|
|
1379
|
+
``analyzer_versions`` family whose members span full-precision,
|
|
1380
|
+
FP8, and GPTQ-Int4 builds, where only the quantized members need
|
|
1381
|
+
a ``quantization_config``::
|
|
1382
|
+
|
|
1383
|
+
def model_load_kwargs(self, model_id, revision):
|
|
1384
|
+
kw = super().model_load_kwargs(model_id, revision)
|
|
1385
|
+
if model_id.endswith('-GPTQ-Int4'):
|
|
1386
|
+
kw['quantization_config'] = ... # gptq config
|
|
1387
|
+
return kw
|
|
1388
|
+
|
|
1389
|
+
:param model_id: resolved HF model id (no ``@revision`` suffix).
|
|
1390
|
+
:param revision: resolved commit/revision being loaded.
|
|
1391
|
+
:return: ``model_kwargs`` dict for this variant.
|
|
1392
|
+
"""
|
|
1393
|
+
return dict(self.MODEL_KWARGS or {})
|
|
1394
|
+
|
|
1300
1395
|
def generate(
|
|
1301
1396
|
self,
|
|
1302
1397
|
prompt: List[str],
|
|
@@ -1341,6 +1436,7 @@ class ClamsHFPromptableApp(ClamsPromptableApp):
|
|
|
1341
1436
|
if n == 0:
|
|
1342
1437
|
return []
|
|
1343
1438
|
gen_kwargs = self.build_gen_kwargs(**generation_params)
|
|
1439
|
+
template_kwargs = self.build_template_kwargs(**generation_params)
|
|
1344
1440
|
try:
|
|
1345
1441
|
conversations = [
|
|
1346
1442
|
self.build_conversation(
|
|
@@ -1357,6 +1453,7 @@ class ClamsHFPromptableApp(ClamsPromptableApp):
|
|
|
1357
1453
|
return_dict=True,
|
|
1358
1454
|
padding=True,
|
|
1359
1455
|
return_tensors="pt",
|
|
1456
|
+
**template_kwargs,
|
|
1360
1457
|
)
|
|
1361
1458
|
inputs = inputs.to(self.device)
|
|
1362
1459
|
if (self.DTYPE is not None
|
|
@@ -1405,6 +1502,28 @@ class ClamsHFPromptableApp(ClamsPromptableApp):
|
|
|
1405
1502
|
})
|
|
1406
1503
|
return gen_kwargs
|
|
1407
1504
|
|
|
1505
|
+
def build_template_kwargs(self, **generation_params) -> dict:
|
|
1506
|
+
"""
|
|
1507
|
+
Extra keyword arguments to pass to
|
|
1508
|
+
``processor.apply_chat_template`` in :py:meth:`generate`, beyond
|
|
1509
|
+
the SDK-fixed ones (``add_generation_prompt``, ``tokenize``,
|
|
1510
|
+
``return_dict``, ``padding``, ``return_tensors``). The base
|
|
1511
|
+
implementation returns ``{}``.
|
|
1512
|
+
|
|
1513
|
+
Override to inject model-specific chat-template controls without
|
|
1514
|
+
having to reimplement :py:meth:`generate`. Common cases:
|
|
1515
|
+
|
|
1516
|
+
* ``{'enable_thinking': False}`` to run a reasoning ("thinking")
|
|
1517
|
+
model in non-thinking mode;
|
|
1518
|
+
* ``{'tools': [...]}`` or ``{'documents': [...]}`` for tool-use /
|
|
1519
|
+
RAG chat templates.
|
|
1520
|
+
|
|
1521
|
+
Keys MUST NOT collide with the SDK-fixed kwargs above. Unknown
|
|
1522
|
+
keys are passed through to the template's render context, where
|
|
1523
|
+
a template that does not reference them simply ignores them.
|
|
1524
|
+
"""
|
|
1525
|
+
return {}
|
|
1526
|
+
|
|
1408
1527
|
|
|
1409
1528
|
class ParameterCaster(object):
|
|
1410
1529
|
|
|
@@ -50,6 +50,8 @@ clams_python.egg-info/SOURCES.txt
|
|
|
50
50
|
clams_python.egg-info/dependency_links.txt
|
|
51
51
|
clams_python.egg-info/entry_points.txt
|
|
52
52
|
clams_python.egg-info/requires.txt
|
|
53
|
+
clams_python.egg-info/scm_file_list.json
|
|
54
|
+
clams_python.egg-info/scm_version.json
|
|
53
55
|
clams_python.egg-info/top_level.txt
|
|
54
56
|
container/Containerfile
|
|
55
57
|
container/ffmpeg-hf.containerfile
|
|
@@ -87,6 +89,7 @@ documentation/target-versions.csv
|
|
|
87
89
|
documentation/tutorial.md
|
|
88
90
|
documentation/autodoc/clams.app.rst
|
|
89
91
|
documentation/autodoc/clams.appmetadata.rst
|
|
92
|
+
documentation/autodoc/clams.backends.rst
|
|
90
93
|
documentation/autodoc/clams.mmif_utils.rst
|
|
91
94
|
documentation/autodoc/clams.restify.rst
|
|
92
95
|
tests/metadata.py
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
{
|
|
2
|
+
"files": [
|
|
3
|
+
"README.md",
|
|
4
|
+
"LICENSE",
|
|
5
|
+
"pyproject.toml",
|
|
6
|
+
".pytype.cfg",
|
|
7
|
+
"CHANGELOG.md",
|
|
8
|
+
"CONTRIBUTING.md",
|
|
9
|
+
".gitignore",
|
|
10
|
+
"build-tools/publish.py",
|
|
11
|
+
"build-tools/prep_release.py",
|
|
12
|
+
"build-tools/build.py",
|
|
13
|
+
"build-tools/docs.py",
|
|
14
|
+
"build-tools/clean.py",
|
|
15
|
+
"build-tools/test.py",
|
|
16
|
+
"clams/__init__.py",
|
|
17
|
+
"clams/develop/__init__.py",
|
|
18
|
+
"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
|
+
"clams/develop/templates/gha/workflows/issue-apps-project.yml.template",
|
|
22
|
+
"clams/develop/templates/gha/workflows/issue-close.yml.template",
|
|
23
|
+
"clams/develop/templates/utl-tf/__init__.py.template",
|
|
24
|
+
"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
|
+
"clams/envelop/__init__.py",
|
|
36
|
+
"clams/appmetadata/__init__.py",
|
|
37
|
+
"clams/app/__init__.py",
|
|
38
|
+
"clams/serve/__init__.py",
|
|
39
|
+
"clams/ver/__init__.py",
|
|
40
|
+
"clams/backends/__init__.py",
|
|
41
|
+
"clams/backends/hf.py",
|
|
42
|
+
"documentation/modules.rst",
|
|
43
|
+
"documentation/target-versions.csv",
|
|
44
|
+
"documentation/app-baseclasses.rst",
|
|
45
|
+
"documentation/gpu-apps.rst",
|
|
46
|
+
"documentation/clamsapp.rst",
|
|
47
|
+
"documentation/appdirectory.rst",
|
|
48
|
+
"documentation/index.rst",
|
|
49
|
+
"documentation/tutorial.md",
|
|
50
|
+
"documentation/introduction.rst",
|
|
51
|
+
"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
|
+
"documentation/autodoc/clams.app.rst",
|
|
58
|
+
"documentation/autodoc/clams.backends.rst",
|
|
59
|
+
"documentation/autodoc/clams.mmif_utils.rst",
|
|
60
|
+
"documentation/autodoc/clams.appmetadata.rst",
|
|
61
|
+
"tests/test_promptable.py",
|
|
62
|
+
"tests/pillow-logo.png",
|
|
63
|
+
"tests/test_backends_hf.py",
|
|
64
|
+
"tests/test_envelope.py",
|
|
65
|
+
"tests/metadata.py",
|
|
66
|
+
"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"
|
|
95
|
+
]
|
|
96
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"``clams-python`` version","``mmif-python`` version","Target MMIF Specification"
|
|
2
|
+
`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/>`__
|
|
2
3
|
`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/>`__
|
|
3
4
|
`1.7.0 <https://pypi.org/project/clams-python/1.7.0/>`__,`1.5.0 <https://pypi.org/project/mmif-python/1.5.0/>`__,`1.2.0 <https://mmif.clams.ai/1.2.0/>`__
|
|
4
5
|
`1.6.0 <https://pypi.org/project/clams-python/1.6.0/>`__,`1.4.0 <https://pypi.org/project/mmif-python/1.4.0/>`__,`1.1.1 <https://mmif.clams.ai/1.1.1/>`__
|
|
@@ -10,7 +10,7 @@ single-turn / turn-taking / user-only modes, and the
|
|
|
10
10
|
"""
|
|
11
11
|
import unittest
|
|
12
12
|
|
|
13
|
-
from mmif import AnnotationTypes, DocumentTypes, Mmif
|
|
13
|
+
from mmif import AnnotationTypes, Document, DocumentTypes, Mmif
|
|
14
14
|
|
|
15
15
|
from clams import AppMetadata, ClamsPromptableApp
|
|
16
16
|
|
|
@@ -326,6 +326,19 @@ class TestStoreResponse(unittest.TestCase):
|
|
|
326
326
|
def setUp(self):
|
|
327
327
|
self.app = make_test_app(make_metadata(call_helper=True))
|
|
328
328
|
self.mmif = Mmif(validate=False)
|
|
329
|
+
# upstream: a media document and an annotation anchored to it, which
|
|
330
|
+
# serves as the response's `source`.
|
|
331
|
+
vdoc = Document()
|
|
332
|
+
vdoc.at_type = DocumentTypes.VideoDocument
|
|
333
|
+
vdoc.id = 'v1'
|
|
334
|
+
vdoc.location = 'file:///video.mp4'
|
|
335
|
+
self.mmif.add_document(vdoc)
|
|
336
|
+
self.doc_id = vdoc.id
|
|
337
|
+
src_view = self.mmif.new_view()
|
|
338
|
+
src_view.metadata.app = 'http://upstream/1'
|
|
339
|
+
self.src = src_view.new_annotation(
|
|
340
|
+
AnnotationTypes.TimeFrame, document=vdoc.id, label='scene')
|
|
341
|
+
# the current app's view
|
|
329
342
|
self.view = self.mmif.new_view()
|
|
330
343
|
self.app.sign_view(self.view, {})
|
|
331
344
|
self.view.new_contain(DocumentTypes.TextDocument)
|
|
@@ -333,22 +346,34 @@ class TestStoreResponse(unittest.TestCase):
|
|
|
333
346
|
|
|
334
347
|
def test_happy_path_creates_textdocument_and_alignment(self):
|
|
335
348
|
td, align = self.app.response_to_grounded_textdocument(
|
|
336
|
-
self.view, source=
|
|
349
|
+
self.view, source=self.src.id, response='generated text')
|
|
337
350
|
self.assertEqual(td.text_value, 'generated text')
|
|
338
|
-
|
|
351
|
+
# the TD inherits the source annotation's document
|
|
352
|
+
self.assertEqual(td.get_property('document'), self.doc_id)
|
|
353
|
+
self.assertEqual(align.get_property('source'), self.src.id)
|
|
339
354
|
self.assertEqual(align.get_property('target'), td.id)
|
|
340
355
|
|
|
341
|
-
def
|
|
342
|
-
# no
|
|
343
|
-
self.
|
|
344
|
-
|
|
356
|
+
def test_source_without_document_raises(self):
|
|
357
|
+
# a source annotation carrying no `document` means a malformed input
|
|
358
|
+
ungrounded = self.view.new_annotation(AnnotationTypes.TimeFrame)
|
|
359
|
+
with self.assertRaises(ValueError):
|
|
360
|
+
self.app.response_to_grounded_textdocument(
|
|
361
|
+
self.view, source=ungrounded.id, response='text')
|
|
362
|
+
|
|
363
|
+
def test_reasoning_trace_none_stores_no_property(self):
|
|
364
|
+
td, _ = self.app.response_to_grounded_textdocument(
|
|
365
|
+
self.view, source=self.src.id, response='text',
|
|
345
366
|
reasoning_trace=None)
|
|
367
|
+
self.assertNotIn('modelReasoningTrace', td.properties)
|
|
346
368
|
|
|
347
|
-
def
|
|
348
|
-
|
|
349
|
-
self.
|
|
350
|
-
|
|
351
|
-
|
|
369
|
+
def test_reasoning_trace_stored_on_textdocument(self):
|
|
370
|
+
td, _ = self.app.response_to_grounded_textdocument(
|
|
371
|
+
self.view, source=self.src.id, response='the answer',
|
|
372
|
+
reasoning_trace='step 1 ... step 2 ...')
|
|
373
|
+
# trace lives in the property; the TD text stays answer-only
|
|
374
|
+
self.assertEqual(
|
|
375
|
+
td.get_property('modelReasoningTrace'), 'step 1 ... step 2 ...')
|
|
376
|
+
self.assertEqual(td.text_value, 'the answer')
|
|
352
377
|
|
|
353
378
|
# TODO (krim @ 05/28/26): this test case belongs upstream in the
|
|
354
379
|
# vocabulary type definition (the `origins`/`origination` pairing
|
|
@@ -359,11 +384,11 @@ class TestStoreResponse(unittest.TestCase):
|
|
|
359
384
|
# underlying TD.
|
|
360
385
|
def test_origins_and_origination_written_together(self):
|
|
361
386
|
td, align = self.app.response_to_grounded_textdocument(
|
|
362
|
-
self.view, source=
|
|
387
|
+
self.view, source=self.src.id, response='caption text',
|
|
363
388
|
origins=['tp1'], origination='derived')
|
|
364
389
|
self.assertEqual(td.get_property('origins'), ['tp1'])
|
|
365
390
|
self.assertEqual(td.get_property('origination'), 'derived')
|
|
366
|
-
self.assertEqual(align.get_property('source'),
|
|
391
|
+
self.assertEqual(align.get_property('source'), self.src.id)
|
|
367
392
|
self.assertEqual(align.get_property('target'), td.id)
|
|
368
393
|
|
|
369
394
|
def test_unpaired_origins_or_origination_raises(self):
|
|
@@ -377,6 +402,51 @@ class TestStoreResponse(unittest.TestCase):
|
|
|
377
402
|
self.view, source='src1', response='text', **kwargs)
|
|
378
403
|
|
|
379
404
|
|
|
405
|
+
# ---------------------------------------------------------------------------
|
|
406
|
+
# Reasoning-trace split helper
|
|
407
|
+
# ---------------------------------------------------------------------------
|
|
408
|
+
|
|
409
|
+
class TestSplitReasoningTrace(unittest.TestCase):
|
|
410
|
+
""":meth:`ClamsPromptableApp.split_tagged_reasoning_trace` (static)."""
|
|
411
|
+
|
|
412
|
+
split = staticmethod(ClamsPromptableApp.split_tagged_reasoning_trace)
|
|
413
|
+
|
|
414
|
+
def test_closed_block_splits_answer_and_trace(self):
|
|
415
|
+
answer, trace = self.split('<think>reasoning</think>The answer.')
|
|
416
|
+
self.assertEqual(answer, 'The answer.')
|
|
417
|
+
self.assertEqual(trace, 'reasoning')
|
|
418
|
+
|
|
419
|
+
def test_no_tags_returns_text_as_answer_none_trace(self):
|
|
420
|
+
answer, trace = self.split('just an answer')
|
|
421
|
+
self.assertEqual(answer, 'just an answer')
|
|
422
|
+
self.assertIsNone(trace)
|
|
423
|
+
|
|
424
|
+
def test_unterminated_block_not_overstripped(self):
|
|
425
|
+
# No closing tag (e.g. ran out of tokens mid-thought): return the
|
|
426
|
+
# raw text rather than discard a possibly-real answer.
|
|
427
|
+
raw = '<think>thinking with no close'
|
|
428
|
+
answer, trace = self.split(raw)
|
|
429
|
+
self.assertEqual(answer, raw)
|
|
430
|
+
self.assertIsNone(trace)
|
|
431
|
+
|
|
432
|
+
def test_uses_final_close_tag(self):
|
|
433
|
+
answer, trace = self.split(
|
|
434
|
+
'<think>a</think>mid<think>b</think>final')
|
|
435
|
+
self.assertEqual(answer, 'final')
|
|
436
|
+
|
|
437
|
+
def test_custom_tags(self):
|
|
438
|
+
answer, trace = self.split(
|
|
439
|
+
'[R]why[/R]done', open_tag='[R]', close_tag='[/R]')
|
|
440
|
+
self.assertEqual(answer, 'done')
|
|
441
|
+
self.assertEqual(trace, 'why')
|
|
442
|
+
|
|
443
|
+
def test_non_thinking_output_safe_to_call(self):
|
|
444
|
+
# Safe to call unconditionally even when thinking is disabled.
|
|
445
|
+
answer, trace = self.split(' plain caption ')
|
|
446
|
+
self.assertEqual(answer, 'plain caption')
|
|
447
|
+
self.assertIsNone(trace)
|
|
448
|
+
|
|
449
|
+
|
|
380
450
|
# ---------------------------------------------------------------------------
|
|
381
451
|
# Transport-neutral parameter casting
|
|
382
452
|
# ---------------------------------------------------------------------------
|
|
@@ -580,6 +650,56 @@ class TestHFPromptableAppClassAttrs(unittest.TestCase):
|
|
|
580
650
|
finally:
|
|
581
651
|
restore()
|
|
582
652
|
|
|
653
|
+
def test_build_template_kwargs_default_empty(self):
|
|
654
|
+
restore, _ = self._patch_load()
|
|
655
|
+
try:
|
|
656
|
+
app = self._make_subclass(analyzer_versions=self.SINGLETON_AV)()
|
|
657
|
+
self.assertEqual(app.build_template_kwargs(), {})
|
|
658
|
+
finally:
|
|
659
|
+
restore()
|
|
660
|
+
|
|
661
|
+
def test_model_load_kwargs_default_is_model_kwargs_copy(self):
|
|
662
|
+
restore, calls = self._patch_load()
|
|
663
|
+
try:
|
|
664
|
+
app = self._make_subclass(
|
|
665
|
+
analyzer_versions=self.SINGLETON_AV,
|
|
666
|
+
MODEL_KWARGS={'trust_remote_code': True},
|
|
667
|
+
)()
|
|
668
|
+
# eager singleton load forwarded the class-level MODEL_KWARGS
|
|
669
|
+
self.assertEqual(
|
|
670
|
+
calls[0]['model_kwargs'], {'trust_remote_code': True})
|
|
671
|
+
# and it's a copy, not the same object
|
|
672
|
+
self.assertIsNot(
|
|
673
|
+
app.model_load_kwargs('org/fake-model', 'deadbee'),
|
|
674
|
+
app.MODEL_KWARGS)
|
|
675
|
+
finally:
|
|
676
|
+
restore()
|
|
677
|
+
|
|
678
|
+
def test_model_load_kwargs_override_is_per_variant(self):
|
|
679
|
+
restore, calls = self._patch_load()
|
|
680
|
+
try:
|
|
681
|
+
def per_variant(self, model_id, revision):
|
|
682
|
+
kw = {'base': True}
|
|
683
|
+
if model_id.endswith('small-model'):
|
|
684
|
+
kw['quantized'] = True
|
|
685
|
+
return kw
|
|
686
|
+
|
|
687
|
+
cls = self._make_subclass(
|
|
688
|
+
analyzer_versions=self.MULTI_AV,
|
|
689
|
+
model_load_kwargs=per_variant,
|
|
690
|
+
)
|
|
691
|
+
app = cls() # multi-member: no eager load
|
|
692
|
+
self.assertEqual(len(calls), 0)
|
|
693
|
+
app.load_model('org/large-model')
|
|
694
|
+
app.load_model('org/small-model')
|
|
695
|
+
large = next(c for c in calls if c['model_id'] == 'org/large-model')
|
|
696
|
+
small = next(c for c in calls if c['model_id'] == 'org/small-model')
|
|
697
|
+
self.assertEqual(large['model_kwargs'], {'base': True})
|
|
698
|
+
self.assertEqual(
|
|
699
|
+
small['model_kwargs'], {'base': True, 'quantized': True})
|
|
700
|
+
finally:
|
|
701
|
+
restore()
|
|
702
|
+
|
|
583
703
|
|
|
584
704
|
if __name__ == '__main__':
|
|
585
705
|
unittest.main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{clams_python-1.7.1 → clams_python-1.7.2}/clams/develop/templates/app/.dockerignore.template
RENAMED
|
File without changes
|
|
File without changes
|
{clams_python-1.7.1 → clams_python-1.7.2}/clams/develop/templates/app/Containerfile.template
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{clams_python-1.7.1 → clams_python-1.7.2}/clams/develop/templates/app/requirements.txt.template
RENAMED
|
File without changes
|
{clams_python-1.7.1 → clams_python-1.7.2}/clams/develop/templates/gha/for-clams-team.md.template
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{clams_python-1.7.1 → clams_python-1.7.2}/clams/develop/templates/gha/workflows/publish.yml.template
RENAMED
|
File without changes
|
{clams_python-1.7.1 → clams_python-1.7.2}/clams/develop/templates/utl-tf/__init__.py.template
RENAMED
|
File without changes
|
{clams_python-1.7.1 → clams_python-1.7.2}/clams/develop/templates/utl-tf/timeframe.py.template
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|