Skip to content

Commit d3312d5

Browse files
Merge pull request #68 from shcherbak-ai/dev
Dev
2 parents 428feb9 + a2bd8e7 commit d3312d5

31 files changed

+9125
-434
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55

66
- **Refactor**: Code reorganization that doesn't change functionality but improves structure or maintainability
77

8+
## [0.17.0](https://github.com/shcherbak-ai/contextgem/releases/tag/v0.17.0) - 2025-08-24
9+
### Added
10+
- Multimodal LLM roles (`"extractor_multimodal"` and `"reasoner_multimodal"`) to support extraction of multimodal document-level concepts from both text and images. Previously, only text and vision roles were supported, requiring choosing either text or image context for extraction, not both.
11+
812
## [0.16.1](https://github.com/shcherbak-ai/contextgem/releases/tag/v0.16.1) - 2025-08-19
913
### Fixed
1014
- Added support for `"minimal"` reasoning effort for gpt-5 models.

contextgem/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
ContextGem - Effortless LLM extraction from documents
2121
"""
2222

23-
__version__ = "0.16.1"
23+
__version__ = "0.17.0"
2424
__author__ = "Shcherbak AI AS"
2525

2626
from contextgem.public import (

contextgem/internal/base/aspects.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ class _Aspect(
8888
default_factory=list,
8989
description=(
9090
"Concepts associated with this aspect. Must be unique by name and description. "
91-
"Concepts with vision LLM roles are not allowed."
91+
"Concepts with vision or multimodal LLM roles are not allowed."
9292
),
9393
) # using Sequence field with list validator for type checking
9494
llm_role: LLMRoleAspect = Field(
@@ -154,16 +154,19 @@ def _validate_concepts_in_aspect(cls, concepts: list[_Concept]) -> list[_Concept
154154
:type concepts: list[_Concept]
155155
:raises ValueError: If multiple concepts have the same name.
156156
:raises ValueError: If multiple concepts have the same description.
157-
:raises ValueError: If any concept has an LLM role ending with '_vision'.
157+
:raises ValueError: If any concept has an LLM role ending with '_vision'
158+
or '_multimodal'.
158159
:return: The validated list of '_Concept' instances.
159160
:rtype: list[_Concept]
160161
"""
161162

162-
if concepts and any(i.llm_role.endswith("_vision") for i in concepts):
163+
if concepts and any(
164+
i.llm_role.endswith(x) for x in ("_vision", "_multimodal") for i in concepts
165+
):
163166
# Validate for Aspect-specific constraints.
164167
raise ValueError(
165-
"Aspect concepts extraction using vision LLMs is not supported. "
166-
"Vision LLMs can be used only for document concept extraction."
168+
"Aspect-level concepts extraction using vision/multimodal LLMs is not supported. "
169+
"Vision/multimodal LLMs can be used only for document-level concept extraction."
167170
)
168171
return concepts
169172

contextgem/internal/base/concepts.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ class _Concept(_ExtractedItemsAttributeProcessor):
9898
default="extractor_text",
9999
description=(
100100
"LLM role used for this concept. Valid values: 'extractor_text', 'reasoner_text', "
101-
"'extractor_vision', 'reasoner_vision'."
101+
"'extractor_vision', 'reasoner_vision', 'extractor_multimodal', 'reasoner_multimodal'."
102102
),
103103
)
104104
add_references: StrictBool = Field(

contextgem/internal/base/llms.py

Lines changed: 285 additions & 87 deletions
Large diffs are not rendered by default.

contextgem/internal/data_models.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ class _LLMUsageOutputContainer(BaseModel):
118118
:vartype model: str
119119
:ivar role: The role of the model, which must be one of
120120
"extractor_text", "reasoner_text", "extractor_vision",
121-
or "reasoner_vision".
121+
"reasoner_vision", "extractor_multimodal", "reasoner_multimodal".
122122
:vartype role: LLMRoleAny
123123
:ivar is_fallback: Indicates whether the LLM is a fallback model.
124124
:vartype is_fallback: StrictBool
@@ -166,7 +166,7 @@ class _LLMCostOutputContainer(BaseModel):
166166
:vartype model: str
167167
:ivar role: The role of the model in processing, which can be one of:
168168
"extractor_text", "reasoner_text", "extractor_vision",
169-
"reasoner_vision".
169+
"reasoner_vision", "extractor_multimodal", "reasoner_multimodal".
170170
:vartype role: LLMRoleAny
171171
:ivar is_fallback: Indicates if the LLM is a fallback model.
172172
:vartype is_fallback: bool

contextgem/internal/typings/aliases.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,12 @@
4040
]
4141

4242
LLMRoleAny = Literal[
43-
"extractor_text", "reasoner_text", "extractor_vision", "reasoner_vision"
43+
"extractor_text",
44+
"reasoner_text",
45+
"extractor_vision",
46+
"reasoner_vision",
47+
"extractor_multimodal",
48+
"reasoner_multimodal",
4449
]
4550

4651
LLMRoleAspect = Literal["extractor_text", "reasoner_text"]

contextgem/internal/utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -512,8 +512,8 @@ def _suppress_litellm_warnings_context() -> Generator[None, None, None]:
512512

513513
def _suppress_litellm_warnings(func: F) -> F:
514514
"""
515-
Suppresses warnings related to Pydantic and httpx deprecation and serialization
516-
in litellm>1.71.1 (latest available version as of 2025-07-10)
515+
A decorator that suppresses warnings related to Pydantic and httpx deprecation
516+
and serialization in litellm>1.71.1 (latest available version as of 2025-07-10).
517517
518518
This decorator wraps both synchronous and asynchronous functions to suppress
519519
Pydantic and httpx warnings that originate from litellm's internal usage.

contextgem/public/concepts.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ class StringConcept(_StringConcept):
5959
:ivar examples: Example strings illustrating the concept usage.
6060
:vartype examples: list[StringExample]
6161
:ivar llm_role: The role of the LLM responsible for extracting the concept
62-
("extractor_text", "reasoner_text", "extractor_vision", "reasoner_vision").
63-
Defaults to "extractor_text".
62+
("extractor_text", "reasoner_text", "extractor_vision", "reasoner_vision",
63+
"extractor_multimodal", "reasoner_multimodal"). Defaults to "extractor_text".
6464
:vartype llm_role: LLMRoleAny
6565
:ivar add_justifications: Whether to include justifications for extracted items.
6666
:vartype add_justifications: bool
@@ -103,8 +103,8 @@ class BooleanConcept(_BooleanConcept):
103103
:ivar description: A brief description of the concept (non-empty string, stripped).
104104
:vartype description: str
105105
:ivar llm_role: The role of the LLM responsible for extracting the concept
106-
("extractor_text", "reasoner_text", "extractor_vision", "reasoner_vision").
107-
Defaults to "extractor_text".
106+
("extractor_text", "reasoner_text", "extractor_vision", "reasoner_vision",
107+
"extractor_multimodal", "reasoner_multimodal"). Defaults to "extractor_text".
108108
:vartype llm_role: LLMRoleAny
109109
:ivar add_justifications: Whether to include justifications for extracted items.
110110
:vartype add_justifications: bool
@@ -151,8 +151,8 @@ class NumericalConcept(_NumericalConcept):
151151
Defaults to "any" for auto-detection.
152152
:vartype numeric_type: Literal["int", "float", "any"]
153153
:ivar llm_role: The role of the LLM responsible for extracting the concept
154-
("extractor_text", "reasoner_text", "extractor_vision", "reasoner_vision").
155-
Defaults to "extractor_text".
154+
("extractor_text", "reasoner_text", "extractor_vision", "reasoner_vision",
155+
"extractor_multimodal", "reasoner_multimodal"). Defaults to "extractor_text".
156156
:vartype llm_role: LLMRoleAny
157157
:ivar add_justifications: Whether to include justifications for extracted items.
158158
:vartype add_justifications: bool
@@ -199,8 +199,8 @@ class RatingConcept(_RatingConcept):
199199
object (deprecated, will be removed in v1.0.0) or a tuple of (start, end) integers.
200200
:vartype rating_scale: RatingScale | tuple[int, int]
201201
:ivar llm_role: The role of the LLM responsible for extracting the concept
202-
("extractor_text", "reasoner_text", "extractor_vision", "reasoner_vision").
203-
Defaults to "extractor_text".
202+
("extractor_text", "reasoner_text", "extractor_vision", "reasoner_vision",
203+
"extractor_multimodal", "reasoner_multimodal"). Defaults to "extractor_text".
204204
:vartype llm_role: LLMRoleAny
205205
:ivar add_justifications: Whether to include justifications for extracted items.
206206
:vartype add_justifications: bool
@@ -277,8 +277,8 @@ class JsonObjectConcept(_JsonObjectConcept):
277277
:ivar examples: Example JSON objects illustrating the concept usage.
278278
:vartype examples: list[JsonObjectExample]
279279
:ivar llm_role: The role of the LLM responsible for extracting the concept
280-
("extractor_text", "reasoner_text", "extractor_vision", "reasoner_vision").
281-
Defaults to "extractor_text".
280+
("extractor_text", "reasoner_text", "extractor_vision", "reasoner_vision",
281+
"extractor_multimodal", "reasoner_multimodal"). Defaults to "extractor_text".
282282
:vartype llm_role: LLMRoleAny
283283
:ivar add_justifications: Whether to include justifications for extracted items.
284284
:vartype add_justifications: bool
@@ -322,8 +322,8 @@ class DateConcept(_DateConcept):
322322
:ivar description: A brief description of the concept (non-empty string, stripped).
323323
:vartype description: str
324324
:ivar llm_role: The role of the LLM responsible for extracting the concept
325-
("extractor_text", "reasoner_text", "extractor_vision", "reasoner_vision").
326-
Defaults to "extractor_text".
325+
("extractor_text", "reasoner_text", "extractor_vision", "reasoner_vision",
326+
"extractor_multimodal", "reasoner_multimodal"). Defaults to "extractor_text".
327327
:vartype llm_role: LLMRoleAny
328328
:ivar add_justifications: Whether to include justifications for extracted items.
329329
:vartype add_justifications: bool
@@ -383,8 +383,8 @@ class LabelConcept(_LabelConcept):
383383
"multi_label" for multiple label selection. Defaults to "multi_class".
384384
:vartype classification_type: ClassificationType
385385
:ivar llm_role: The role of the LLM responsible for extracting the concept
386-
("extractor_text", "reasoner_text", "extractor_vision", "reasoner_vision").
387-
Defaults to "extractor_text".
386+
("extractor_text", "reasoner_text", "extractor_vision", "reasoner_vision",
387+
"extractor_multimodal", "reasoner_multimodal"). Defaults to "extractor_text".
388388
:vartype llm_role: LLMRoleAny
389389
:ivar add_justifications: Whether to include justifications for extracted items.
390390
:vartype add_justifications: bool

contextgem/public/images.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,9 @@ class Image(_Image):
5353
An image must be attached to a document. A document can have multiple images.
5454
5555
- Extraction types:
56-
Only concept extraction is supported for images. Use LLM with role ``"extractor_vision"``
57-
or ``"reasoner_vision"`` to extract concepts from images.
56+
Only document-level concept extraction is supported for images. Use LLM with role
57+
``"extractor_vision"``, ``"reasoner_vision"``, ``"extractor_multimodal"``,
58+
or ``"reasoner_multimodal"`` to extract concepts from images.
5859
5960
Example:
6061
.. literalinclude:: ../../../dev/usage_examples/docstrings/images/def_image.py

0 commit comments

Comments
 (0)