wuenlp_tools.models.characters

 1from wuenlp_tools.models.characters.maverick_coref import (
 2    MaverickCoreference,
 3    MaverickCoreferenceProcessor,
 4    maverick_coref_api,
 5)
 6from wuenlp_tools.models.characters.llm_alias_coref import (
 7    LLMAliasCoref,
 8    LLMAliasCorefConfig,
 9    LLMAliasCorefPipelineStep,
10    LLMAliasCorefProcessor,
11    CharacterAliasEntry,
12    CharacterAliasOutput,
13    ensure_whitespace_segmentation,
14)
15from wuenlp_tools.models.characters.main_characters import (
16    MainCharacterExtractor,
17    MainCharacterExtractorProcessor,
18)
19
20__all__ = [
21    "MaverickCoreference",
22    "MaverickCoreferenceProcessor",
23    "maverick_coref_api",
24    "LLMAliasCoref",
25    "LLMAliasCorefConfig",
26    "LLMAliasCorefPipelineStep",
27    "LLMAliasCorefProcessor",
28    "CharacterAliasEntry",
29    "CharacterAliasOutput",
30    "ensure_whitespace_segmentation",
31    "MainCharacterExtractor",
32    "MainCharacterExtractorProcessor",
33]
MaverickCoreference = PipelineStep('Maverick Coreference', processor=MaverickCoreferenceProcessor, provides=['coref', 'characters'], requires=['preprocess'])

Pipeline step Maverick Coreference (MaverickCoreferenceProcessor).

Provides: coref, characters

  • coref: Provides coreference annotations (UIMAEntity, UIMAEntityReference) needed by character-related steps.
  • characters: Provides UIMACharacter annotations derived from entity/coreference information.

Requires: preprocess

modifies UIMAEntity, UIMAEntityReference.

57class MaverickCoreferenceProcessor(PipelineProcessor):
58    def __init__(self, url: str = DEFAULT_MAVERICK_COREF_URL):
59        self.url = url
60
61    def __call__(
62        self,
63        doc: UIMADocument,
64        unit_type: Type[UIMASpan] | None = None,
65        overwrite: bool = False,
66        **kwargs,
67    ) -> UIMADocument:
68        return maverick_coref_api(doc, overwrite=overwrite, url=self.url)

Base class for protocol classes.

Protocol classes are defined as::

class Proto(Protocol):
    def meth(self) -> int:
        ...

Such classes are primarily used with static type checkers that recognize structural subtyping (static duck-typing).

For example::

class C:
    def meth(self) -> int:
        return 0

def func(x: Proto) -> int:
    return x.meth()

func(C())  # Passes static type check

See PEP 544 for details. Protocol classes decorated with @typing.runtime_checkable act as simple-minded runtime protocols that check only the presence of given attributes, ignoring their type signatures. Protocol classes can be generic, they are defined as::

class GenProto[T](Protocol):
    def meth(self) -> T:
        ...
MaverickCoreferenceProcessor( url: str = 'http://lsx-alpine-proxy.informatik.uni-wuerzburg.de:29080/process/e2e')
58    def __init__(self, url: str = DEFAULT_MAVERICK_COREF_URL):
59        self.url = url
url
def maverick_coref_api( doc: wuenlp.impl.uima.UIMANLPStructs.UIMADocument, overwrite: bool = False, url: str = 'http://lsx-alpine-proxy.informatik.uni-wuerzburg.de:29080/process/e2e') -> wuenlp.impl.uima.UIMANLPStructs.UIMADocument:
31def maverick_coref_api(
32    doc: UIMADocument,
33    overwrite: bool = False,
34    url: str = DEFAULT_MAVERICK_COREF_URL,
35) -> UIMADocument:
36    if _has_coreference_annotations(doc) and not overwrite:
37        logger.info(f"Document {doc} already has coreference annotations. Skipping")
38        return doc
39
40    document_full_extension = ".xmi.zip"
41    with tempfile.NamedTemporaryFile(suffix=document_full_extension, delete=False) as temp_file:
42        doc.serialize(temp_file.name)
43
44    with open(temp_file.name, "rb") as file:
45        files = {"file": (Path(temp_file.name).name, file)}
46        logger.info(f"Sending file to Maverick coreference service at {url}")
47        response = query_wuenlp_api(url=url, files=files)
48
49    suffix = _response_suffix(response.content)
50    with tempfile.NamedTemporaryFile(suffix=suffix, delete=False) as temp_out_file:
51        with open(temp_out_file.name, "wb") as f:
52            f.write(response.content)
53
54    return UIMADocument.from_xmi(temp_out_file.name, ignore_unknown_types=True)
LLMAliasCoref = PipelineStep('LLM Alias Coref', processor=LLMAliasCorefProcessor, provides=['coref', 'characters'], requires=['preprocess'])

Pipeline step LLM Alias Coref (LLMAliasCorefProcessor).

Provides: coref, characters

  • coref: Provides coreference annotations (UIMAEntity, UIMAEntityReference) needed by character-related steps.
  • characters: Provides UIMACharacter annotations derived from entity/coreference information.

Requires: preprocess

modifies UIMAEntity, UIMAEntityReference, UIMACharacter, UIMACharacterReference; additional features llm_aliases, llm_anti_references.

@dataclass(frozen=True)
class LLMAliasCorefConfig:
53@dataclass(frozen=True)
54class LLMAliasCorefConfig:
55    model: LLMArchitecture = field(default_factory=lambda: gemini31lite)
56    n_characters: int = 20
57    system_prompt: str = DEFAULT_LLM_ALIAS_COREF_SYSTEM_PROMPT
58    base_url: str = "https://ollama.professor-x.de/v1/"
59    case_sensitive: bool = True
60    join_tokens_with: str = " "
LLMAliasCorefConfig( model: wuenlp_tools.utils.prompting.LLMArchitecture = <factory>, n_characters: int = 20, system_prompt: str = 'You analyze novels. Given the full text, identify the N most important characters.\n\nFor each character return:\n- canonical_name: full proper name (e.g. "Harry Potter")\n- aliases: unique surface forms that refer ONLY to that character in the text\n- anti_references: surface forms that must NOT be linked to this character\n\nAlias rules:\n- Include first names, nicknames, epithets, and definite descriptions that uniquely identify this character.\n- Do NOT include family names or surnames alone when they can refer to multiple characters (e.g. do not list "Weasley" for Ron Weasley).\n- Do NOT include generic titles or words shared by many people unless clearly unique to this character.\n- Each alias must be an exact phrase that can appear in the novel.\n- Do not assign the same alias to more than one character.\n\nAnti-reference rules:\n- Use anti_references sparingly: only phrases that are definitely NOT this character but would otherwise be matched because they share tokens with an alias (e.g. a surname inside a longer phrase).\n- Example: for Harry Potter, "Mrs. Potter" is a valid anti-reference (unambiguously his mother), but "Mr. Potter" is not — Harry is also called that in the text.\n- Do NOT list ambiguous forms that are used to refer to this character in the text.\n- Each anti-reference must be an exact phrase that can appear in the novel.\n- Do not list phrases that are already valid aliases for this character.\n- When unsure, leave anti_references empty for that case.\n\nReturn valid JSON matching the required schema.', base_url: str = 'https://ollama.professor-x.de/v1/', case_sensitive: bool = True, join_tokens_with: str = ' ')
n_characters: int = 20

int([x]) -> integer int(x, base=10) -> integer

Convert a number or string to an integer, or return 0 if no arguments are given. If x is a number, return x.__int__(). For floating-point numbers, this truncates towards zero.

If x is not a number or if base is given, then x must be a string, bytes, or bytearray instance representing an integer literal in the given base. The literal can be preceded by '+' or '-' and be surrounded by whitespace. The base defaults to 10. Valid bases are 0 and 2-36. Base 0 means to interpret the base from the string as an integer literal.

>>> int('0b100', base=0)
4
system_prompt: str = 'You analyze novels. Given the full text, identify the N most important characters.\n\nFor each character return:\n- canonical_name: full proper name (e.g. "Harry Potter")\n- aliases: unique surface forms that refer ONLY to that character in the text\n- anti_references: surface forms that must NOT be linked to this character\n\nAlias rules:\n- Include first names, nicknames, epithets, and definite descriptions that uniquely identify this character.\n- Do NOT include family names or surnames alone when they can refer to multiple characters (e.g. do not list "Weasley" for Ron Weasley).\n- Do NOT include generic titles or words shared by many people unless clearly unique to this character.\n- Each alias must be an exact phrase that can appear in the novel.\n- Do not assign the same alias to more than one character.\n\nAnti-reference rules:\n- Use anti_references sparingly: only phrases that are definitely NOT this character but would otherwise be matched because they share tokens with an alias (e.g. a surname inside a longer phrase).\n- Example: for Harry Potter, "Mrs. Potter" is a valid anti-reference (unambiguously his mother), but "Mr. Potter" is not — Harry is also called that in the text.\n- Do NOT list ambiguous forms that are used to refer to this character in the text.\n- Each anti-reference must be an exact phrase that can appear in the novel.\n- Do not list phrases that are already valid aliases for this character.\n- When unsure, leave anti_references empty for that case.\n\nReturn valid JSON matching the required schema.'

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

base_url: str = 'https://ollama.professor-x.de/v1/'

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

case_sensitive: bool = True

bool(x) -> bool

Returns True when the argument x is true, False otherwise. The builtins True and False are the only two instances of the class bool. The class bool is a subclass of the class int, and cannot be subclassed.

join_tokens_with: str = ' '

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

def LLMAliasCorefPipelineStep( config: LLMAliasCorefConfig | None = None, name: str = 'LLM Alias Coref') -> wuenlp_tools.pipeline.PipelineStep[LLMAliasCorefProcessor]:
431def LLMAliasCorefPipelineStep(
432    config: LLMAliasCorefConfig | None = None,
433    name: str = "LLM Alias Coref",
434) -> PipelineStep[LLMAliasCorefProcessor]:
435    cfg = config or LLMAliasCorefConfig()
436    return PipelineStep(
437        name=name,
438        processor=LLMAliasCorefProcessor(config=cfg),
439        unit_type=None,
440        modified_types=[UIMAEntity, UIMAEntityReference, UIMACharacter, UIMACharacterReference],
441        added_additional_features=list(LLM_ALIAS_COREF_ENTITY_FEATURES),
442        requires_api_key=OPENAI_API_KEY,
443        requires_paid_api_requests=True,
444        provides=[PipelineCapability.COREF, PipelineCapability.CHARACTERS],
445        requires=[PipelineCapability.PREPROCESS],
446    )
417class LLMAliasCorefProcessor(PipelineProcessor):
418    def __init__(self, config: LLMAliasCorefConfig | None = None):
419        self.config = config or LLMAliasCorefConfig()
420
421    def __call__(
422        self,
423        doc: UIMADocument,
424        unit_type: Type[UIMASpan] | None = None,
425        overwrite: bool = False,
426        **kwargs,
427    ) -> UIMADocument:
428        return llm_alias_coref(doc, overwrite=overwrite, config=self.config)

Base class for protocol classes.

Protocol classes are defined as::

class Proto(Protocol):
    def meth(self) -> int:
        ...

Such classes are primarily used with static type checkers that recognize structural subtyping (static duck-typing).

For example::

class C:
    def meth(self) -> int:
        return 0

def func(x: Proto) -> int:
    return x.meth()

func(C())  # Passes static type check

See PEP 544 for details. Protocol classes decorated with @typing.runtime_checkable act as simple-minded runtime protocols that check only the presence of given attributes, ignoring their type signatures. Protocol classes can be generic, they are defined as::

class GenProto[T](Protocol):
    def meth(self) -> T:
        ...
LLMAliasCorefProcessor( config: LLMAliasCorefConfig | None = None)
418    def __init__(self, config: LLMAliasCorefConfig | None = None):
419        self.config = config or LLMAliasCorefConfig()
config
class CharacterAliasEntry(pydantic.main.BaseModel):
43class CharacterAliasEntry(BaseModel):
44    canonical_name: str
45    aliases: list[str] = Field(default_factory=list)
46    anti_references: list[str] = Field(default_factory=list)

!!! abstract "Usage Documentation" Models

A base class for creating Pydantic models.

Attributes: __class_vars__: The names of the class variables defined on the model. __private_attributes__: Metadata about the private attributes of the model. __signature__: The synthesized __init__ [Signature][inspect.Signature] of the model.

__pydantic_complete__: Whether model building is completed, or if there are still undefined fields.
__pydantic_core_schema__: The core schema of the model.
__pydantic_custom_init__: Whether the model has a custom `__init__` function.
__pydantic_decorators__: Metadata containing the decorators defined on the model.
    This replaces `Model.__validators__` and `Model.__root_validators__` from Pydantic V1.
__pydantic_generic_metadata__: A dictionary containing metadata about generic Pydantic models.
    The `origin` and `args` items map to the [`__origin__`][genericalias.__origin__]
    and [`__args__`][genericalias.__args__] attributes of [generic aliases][types-genericalias],
    and the `parameter` item maps to the `__parameter__` attribute of generic classes.
__pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models.
__pydantic_post_init__: The name of the post-init method for the model, if defined.
__pydantic_root_model__: Whether the model is a [`RootModel`][pydantic.root_model.RootModel].
__pydantic_serializer__: The `pydantic-core` `SchemaSerializer` used to dump instances of the model.
__pydantic_validator__: The `pydantic-core` `SchemaValidator` used to validate instances of the model.

__pydantic_fields__: A dictionary of field names and their corresponding [`FieldInfo`][pydantic.fields.FieldInfo] objects.
__pydantic_computed_fields__: A dictionary of computed field names and their corresponding [`ComputedFieldInfo`][pydantic.fields.ComputedFieldInfo] objects.

__pydantic_extra__: A dictionary containing extra values, if [`extra`][pydantic.config.ConfigDict.extra]
    is set to `'allow'`.
__pydantic_fields_set__: The names of fields explicitly set during instantiation.
__pydantic_private__: Values of private attributes set on the model instance.
canonical_name: str = PydanticUndefined
aliases: list[str] = PydanticUndefined
anti_references: list[str] = PydanticUndefined
class CharacterAliasOutput(pydantic.main.BaseModel):
49class CharacterAliasOutput(BaseModel):
50    characters: list[CharacterAliasEntry] = Field(default_factory=list)

!!! abstract "Usage Documentation" Models

A base class for creating Pydantic models.

Attributes: __class_vars__: The names of the class variables defined on the model. __private_attributes__: Metadata about the private attributes of the model. __signature__: The synthesized __init__ [Signature][inspect.Signature] of the model.

__pydantic_complete__: Whether model building is completed, or if there are still undefined fields.
__pydantic_core_schema__: The core schema of the model.
__pydantic_custom_init__: Whether the model has a custom `__init__` function.
__pydantic_decorators__: Metadata containing the decorators defined on the model.
    This replaces `Model.__validators__` and `Model.__root_validators__` from Pydantic V1.
__pydantic_generic_metadata__: A dictionary containing metadata about generic Pydantic models.
    The `origin` and `args` items map to the [`__origin__`][genericalias.__origin__]
    and [`__args__`][genericalias.__args__] attributes of [generic aliases][types-genericalias],
    and the `parameter` item maps to the `__parameter__` attribute of generic classes.
__pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models.
__pydantic_post_init__: The name of the post-init method for the model, if defined.
__pydantic_root_model__: Whether the model is a [`RootModel`][pydantic.root_model.RootModel].
__pydantic_serializer__: The `pydantic-core` `SchemaSerializer` used to dump instances of the model.
__pydantic_validator__: The `pydantic-core` `SchemaValidator` used to validate instances of the model.

__pydantic_fields__: A dictionary of field names and their corresponding [`FieldInfo`][pydantic.fields.FieldInfo] objects.
__pydantic_computed_fields__: A dictionary of computed field names and their corresponding [`ComputedFieldInfo`][pydantic.fields.ComputedFieldInfo] objects.

__pydantic_extra__: A dictionary containing extra values, if [`extra`][pydantic.config.ConfigDict.extra]
    is set to `'allow'`.
__pydantic_fields_set__: The names of fields explicitly set during instantiation.
__pydantic_private__: Values of private attributes set on the model instance.
characters: list[CharacterAliasEntry] = PydanticUndefined
def ensure_whitespace_segmentation(doc: wuenlp.impl.uima.UIMANLPStructs.UIMADocument) -> tuple[int, int]:
184def ensure_whitespace_segmentation(doc: UIMADocument) -> tuple[int, int]:
185    """Add whitespace tokens and simple sentence spans when missing."""
186    if list(doc.tokens):
187        token_count = len(list(doc.tokens))
188        sentence_count = len(list(doc.sentences))
189        if sentence_count:
190            return token_count, sentence_count
191
192    text = doc.text
193    token_count = 0
194    for match in re.finditer(r"\S+", text):
195        doc.create_token(int(match.start()), int(match.end()), add_to_document=True)
196        token_count += 1
197
198    sentence_count = 0
199    for match in re.finditer(r"[^.!?…]+[.!?…]+(?:\s+|$)|[^\n]+(?:\n{2,}|$)", text):
200        begin = int(match.start())
201        end = int(match.end())
202        if end <= begin:
203            continue
204        doc.create_sentence(begin, end, add_to_document=True)
205        sentence_count += 1
206
207    if sentence_count == 0 and len(text) > 0:
208        doc.create_sentence(0, len(text), add_to_document=True)
209        sentence_count = 1
210
211    logger.info(
212        "Whitespace segmentation: {} tokens, {} sentences (path={})",
213        token_count,
214        sentence_count,
215        doc.path,
216    )
217    return token_count, sentence_count

Add whitespace tokens and simple sentence spans when missing.

MainCharacterExtractor = PipelineStep('Main Character Extractor', processor=MainCharacterExtractorProcessor, provides=['main_characters'], requires=['characters'])

Pipeline step Main Character Extractor (MainCharacterExtractorProcessor).

Provides: main_characters

  • main_characters: Provides main and important characters on document annotation (main_character, important_characters).

Requires: characters

36class MainCharacterExtractorProcessor(PipelineProcessor):
37    def __call__(self, doc: UIMADocument, unit_type: Optional[Type[UIMASpan]] = None, overwrite: bool | None = False,
38                 **kwargs) -> UIMADocument:
39        return extract_main_characters(doc, overwrite=overwrite)

Base class for protocol classes.

Protocol classes are defined as::

class Proto(Protocol):
    def meth(self) -> int:
        ...

Such classes are primarily used with static type checkers that recognize structural subtyping (static duck-typing).

For example::

class C:
    def meth(self) -> int:
        return 0

def func(x: Proto) -> int:
    return x.meth()

func(C())  # Passes static type check

See PEP 544 for details. Protocol classes decorated with @typing.runtime_checkable act as simple-minded runtime protocols that check only the presence of given attributes, ignoring their type signatures. Protocol classes can be generic, they are defined as::

class GenProto[T](Protocol):
    def meth(self) -> T:
        ...