cat-stack 1.6.5__tar.gz → 1.6.6__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.
- {cat_stack-1.6.5 → cat_stack-1.6.6}/PKG-INFO +1 -1
- {cat_stack-1.6.5 → cat_stack-1.6.6}/src/catstack/__about__.py +1 -1
- {cat_stack-1.6.5 → cat_stack-1.6.6}/src/catstack/_providers.py +77 -0
- {cat_stack-1.6.5 → cat_stack-1.6.6}/src/catstack/text_functions_ensemble.py +3 -3
- {cat_stack-1.6.5 → cat_stack-1.6.6}/.gitignore +0 -0
- {cat_stack-1.6.5 → cat_stack-1.6.6}/LICENSE +0 -0
- {cat_stack-1.6.5 → cat_stack-1.6.6}/README.md +0 -0
- {cat_stack-1.6.5 → cat_stack-1.6.6}/pyproject.toml +0 -0
- {cat_stack-1.6.5 → cat_stack-1.6.6}/src/cat_stack/__init__.py +0 -0
- {cat_stack-1.6.5 → cat_stack-1.6.6}/src/catstack/__init__.py +0 -0
- {cat_stack-1.6.5 → cat_stack-1.6.6}/src/catstack/_batch.py +0 -0
- {cat_stack-1.6.5 → cat_stack-1.6.6}/src/catstack/_category_analysis.py +0 -0
- {cat_stack-1.6.5 → cat_stack-1.6.6}/src/catstack/_chunked.py +0 -0
- {cat_stack-1.6.5 → cat_stack-1.6.6}/src/catstack/_embeddings.py +0 -0
- {cat_stack-1.6.5 → cat_stack-1.6.6}/src/catstack/_formatter.py +0 -0
- {cat_stack-1.6.5 → cat_stack-1.6.6}/src/catstack/_pilot_test.py +0 -0
- {cat_stack-1.6.5 → cat_stack-1.6.6}/src/catstack/_prompts.py +0 -0
- {cat_stack-1.6.5 → cat_stack-1.6.6}/src/catstack/_review_ui.py +0 -0
- {cat_stack-1.6.5 → cat_stack-1.6.6}/src/catstack/_tiebreaker.py +0 -0
- {cat_stack-1.6.5 → cat_stack-1.6.6}/src/catstack/_utils.py +0 -0
- {cat_stack-1.6.5 → cat_stack-1.6.6}/src/catstack/_web_fetch.py +0 -0
- {cat_stack-1.6.5 → cat_stack-1.6.6}/src/catstack/_wrapper_helpers.py +0 -0
- {cat_stack-1.6.5 → cat_stack-1.6.6}/src/catstack/calls/CoVe.py +0 -0
- {cat_stack-1.6.5 → cat_stack-1.6.6}/src/catstack/calls/__init__.py +0 -0
- {cat_stack-1.6.5 → cat_stack-1.6.6}/src/catstack/calls/image_CoVe.py +0 -0
- {cat_stack-1.6.5 → cat_stack-1.6.6}/src/catstack/calls/image_stepback.py +0 -0
- {cat_stack-1.6.5 → cat_stack-1.6.6}/src/catstack/calls/pdf_CoVe.py +0 -0
- {cat_stack-1.6.5 → cat_stack-1.6.6}/src/catstack/calls/pdf_stepback.py +0 -0
- {cat_stack-1.6.5 → cat_stack-1.6.6}/src/catstack/calls/stepback.py +0 -0
- {cat_stack-1.6.5 → cat_stack-1.6.6}/src/catstack/calls/top_n.py +0 -0
- {cat_stack-1.6.5 → cat_stack-1.6.6}/src/catstack/classify.py +0 -0
- {cat_stack-1.6.5 → cat_stack-1.6.6}/src/catstack/explore.py +0 -0
- {cat_stack-1.6.5 → cat_stack-1.6.6}/src/catstack/extract.py +0 -0
- {cat_stack-1.6.5 → cat_stack-1.6.6}/src/catstack/image_functions.py +0 -0
- {cat_stack-1.6.5 → cat_stack-1.6.6}/src/catstack/images/circle.png +0 -0
- {cat_stack-1.6.5 → cat_stack-1.6.6}/src/catstack/images/cube.png +0 -0
- {cat_stack-1.6.5 → cat_stack-1.6.6}/src/catstack/images/diamond.png +0 -0
- {cat_stack-1.6.5 → cat_stack-1.6.6}/src/catstack/images/overlapping_pentagons.png +0 -0
- {cat_stack-1.6.5 → cat_stack-1.6.6}/src/catstack/images/rectangles.png +0 -0
- {cat_stack-1.6.5 → cat_stack-1.6.6}/src/catstack/model_reference_list.py +0 -0
- {cat_stack-1.6.5 → cat_stack-1.6.6}/src/catstack/pdf_functions.py +0 -0
- {cat_stack-1.6.5 → cat_stack-1.6.6}/src/catstack/prompt_tune.py +0 -0
- {cat_stack-1.6.5 → cat_stack-1.6.6}/src/catstack/summarize.py +0 -0
- {cat_stack-1.6.5 → cat_stack-1.6.6}/src/catstack/text_functions.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cat-stack
|
|
3
|
-
Version: 1.6.
|
|
3
|
+
Version: 1.6.6
|
|
4
4
|
Summary: Domain-agnostic text, image, PDF, and DOCX classification engine powered by LLMs
|
|
5
5
|
Project-URL: Documentation, https://github.com/chrissoria/cat-stack#readme
|
|
6
6
|
Project-URL: Issues, https://github.com/chrissoria/cat-stack/issues
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# SPDX-FileCopyrightText: 2025-present Christopher Soria <chrissoria@berkeley.edu>
|
|
2
2
|
#
|
|
3
3
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
|
-
__version__ = "1.6.
|
|
4
|
+
__version__ = "1.6.6"
|
|
5
5
|
__author__ = "Chris Soria"
|
|
6
6
|
__email__ = "chrissoria@berkeley.edu"
|
|
7
7
|
__title__ = "cat-stack"
|
|
@@ -125,6 +125,64 @@ _HF_NEEDS_ENABLE_THINKING_OFF = (
|
|
|
125
125
|
def _hf_model_needs_enable_thinking_off(model: str) -> bool:
|
|
126
126
|
return any(model.startswith(p) for p in _HF_NEEDS_ENABLE_THINKING_OFF)
|
|
127
127
|
|
|
128
|
+
|
|
129
|
+
# ---------------------------------------------------------------------------
|
|
130
|
+
# Ollama reasoning control: per-model-family parameter format for the
|
|
131
|
+
# top-level `think` field on chat / generate requests.
|
|
132
|
+
#
|
|
133
|
+
# Ollama standardized on a single API field name (`think`) but the value
|
|
134
|
+
# type differs per model family — gpt-oss takes an enum, most others take
|
|
135
|
+
# a boolean. See https://docs.ollama.com/capabilities/thinking.
|
|
136
|
+
#
|
|
137
|
+
# Coverage philosophy: list every Ollama reasoning model family we know of
|
|
138
|
+
# AND that uses the `think` field. Reasoning models that gate via other
|
|
139
|
+
# mechanisms (system prompts, chat-template flags) are explicitly noted in
|
|
140
|
+
# the "NOT in registry" comment below and handled elsewhere — adding them
|
|
141
|
+
# here would silently inject a no-op `think` field, which Ollama may
|
|
142
|
+
# accept but won't honor, leading to surprising behavior.
|
|
143
|
+
#
|
|
144
|
+
# Entries are checked longest-prefix-first by `_ollama_think_value()`, so
|
|
145
|
+
# put more-specific prefixes earlier when adding (e.g. `qwen3-coder` before
|
|
146
|
+
# `qwen3` if they differ).
|
|
147
|
+
#
|
|
148
|
+
# Registry tuple: (model prefix, value-format, low_value, high_value)
|
|
149
|
+
#
|
|
150
|
+
# Models in registry — `think` field works:
|
|
151
|
+
# gpt-oss — enum: "low" / "medium" / "high" (cannot fully disable)
|
|
152
|
+
# qwen3 / qwen3.* — bool: True / False (covers -thinking variants too)
|
|
153
|
+
# qwq — bool: True / False (Qwen QwQ — preceded Qwen3)
|
|
154
|
+
# deepseek-r1 — bool: True / False (covers -distill variants)
|
|
155
|
+
#
|
|
156
|
+
# Models NOT in registry — different mechanism, do NOT add here:
|
|
157
|
+
# magistral — controlled via system prompt (Mistral Magistral)
|
|
158
|
+
# exaone-deep — uses Modelfile-baked reasoning, no API toggle exposed
|
|
159
|
+
# marco-o1 — uses chat-template wrappers, not `think` field
|
|
160
|
+
#
|
|
161
|
+
# Models with NO reasoning (so `think` should not appear at all):
|
|
162
|
+
# gemma2/3, llama3.x/4.x, mistral, mistral-nemo, qwen2.5 (non-QwQ),
|
|
163
|
+
# phi3/4, granite, olmo, codestral, …
|
|
164
|
+
# These are NOT added; the registry's None-return for unmatched prefixes
|
|
165
|
+
# correctly omits the `think` field for them.
|
|
166
|
+
# ---------------------------------------------------------------------------
|
|
167
|
+
_OLLAMA_REASONING_MODELS = (
|
|
168
|
+
("gpt-oss", "enum", "low", "high"),
|
|
169
|
+
("qwen3", "bool", False, True), # covers qwen3.*, qwen3-*, -thinking-* variants
|
|
170
|
+
("qwq", "bool", False, True),
|
|
171
|
+
("deepseek-r1", "bool", False, True), # covers -distill-qwen, -distill-llama, etc.
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _ollama_think_value(model: str, thinking_budget):
|
|
176
|
+
"""Map cat-stack's thinking_budget to the right Ollama `think` value for
|
|
177
|
+
this model family. Returns None if the model isn't in the
|
|
178
|
+
reasoning-capable registry (no `think` field should be set)."""
|
|
179
|
+
if thinking_budget is None:
|
|
180
|
+
return None
|
|
181
|
+
for prefix, fmt, low_val, high_val in _OLLAMA_REASONING_MODELS:
|
|
182
|
+
if model.startswith(prefix):
|
|
183
|
+
return low_val if thinking_budget == 0 else high_val
|
|
184
|
+
return None
|
|
185
|
+
|
|
128
186
|
__all__ = [
|
|
129
187
|
# Main client
|
|
130
188
|
"UnifiedLLMClient",
|
|
@@ -457,6 +515,12 @@ class UnifiedLLMClient:
|
|
|
457
515
|
elif self.provider in ("huggingface", "huggingface-together"):
|
|
458
516
|
# HuggingFace needs thinking_budget to disable thinking on models that reason by default
|
|
459
517
|
return self._build_openai_payload(messages, json_schema, creativity, force_json, thinking_budget)
|
|
518
|
+
elif self.provider == "ollama":
|
|
519
|
+
# Ollama threads thinking_budget to its top-level `think` field for
|
|
520
|
+
# reasoning-capable models (gpt-oss accepts low/medium/high; others
|
|
521
|
+
# accept booleans). Without this, gpt-oss family models emit long
|
|
522
|
+
# <think> blocks by default that bloat per-row generation 3-5x.
|
|
523
|
+
return self._build_openai_payload(messages, json_schema, creativity, force_json, thinking_budget)
|
|
460
524
|
else:
|
|
461
525
|
# Other OpenAI-compatible providers (xai, mistral, etc.)
|
|
462
526
|
return self._build_openai_payload(messages, json_schema, creativity, force_json)
|
|
@@ -532,6 +596,19 @@ class UnifiedLLMClient:
|
|
|
532
596
|
elif creativity is not None:
|
|
533
597
|
payload["temperature"] = creativity
|
|
534
598
|
|
|
599
|
+
# Ollama: per-model-family reasoning control via the top-level
|
|
600
|
+
# `think` field. gpt-oss expects an enum ("low"/"medium"/"high");
|
|
601
|
+
# qwen3/deepseek-r1 expect a boolean. Models not in the
|
|
602
|
+
# `_OLLAMA_REASONING_MODELS` registry don't support reasoning and
|
|
603
|
+
# get no `think` field (would be a no-op at best, validator-
|
|
604
|
+
# confusing at worst). Without this, Ollama-served gpt-oss
|
|
605
|
+
# produces long `<think>` blocks by default that bloat per-row
|
|
606
|
+
# generation 3-5x.
|
|
607
|
+
if self.provider == "ollama":
|
|
608
|
+
think_value = _ollama_think_value(self.model, thinking_budget)
|
|
609
|
+
if think_value is not None:
|
|
610
|
+
payload["think"] = think_value
|
|
611
|
+
|
|
535
612
|
# HuggingFace: disable thinking on model families whose chat
|
|
536
613
|
# template honors `enable_thinking` (Qwen3-family). Other HF-routed
|
|
537
614
|
# models don't need the kwarg, and strict-validator backends
|
|
@@ -3043,7 +3043,7 @@ Categorize text responses {cove_categorize}:
|
|
|
3043
3043
|
messages=messages,
|
|
3044
3044
|
json_schema=json_schemas[cfg["model"]],
|
|
3045
3045
|
creativity=effective_creativity,
|
|
3046
|
-
thinking_budget=thinking_budget if cfg["provider"] in ("google", "openai", "anthropic", "huggingface", "huggingface-together") else None,
|
|
3046
|
+
thinking_budget=thinking_budget if cfg["provider"] in ("google", "openai", "anthropic", "huggingface", "huggingface-together", "ollama") else None,
|
|
3047
3047
|
max_retries=max_retries,
|
|
3048
3048
|
)
|
|
3049
3049
|
|
|
@@ -3100,7 +3100,7 @@ Categorize text responses {cove_categorize}:
|
|
|
3100
3100
|
messages=messages,
|
|
3101
3101
|
json_schema=json_schemas[cfg["model"]],
|
|
3102
3102
|
creativity=effective_creativity,
|
|
3103
|
-
thinking_budget=thinking_budget if cfg["provider"] in ("google", "openai", "anthropic", "huggingface", "huggingface-together") else None,
|
|
3103
|
+
thinking_budget=thinking_budget if cfg["provider"] in ("google", "openai", "anthropic", "huggingface", "huggingface-together", "ollama") else None,
|
|
3104
3104
|
max_retries=max_retries,
|
|
3105
3105
|
)
|
|
3106
3106
|
|
|
@@ -3184,7 +3184,7 @@ Categorize text responses {cove_categorize}:
|
|
|
3184
3184
|
messages=_retry_messages,
|
|
3185
3185
|
json_schema=json_schemas[cfg["model"]],
|
|
3186
3186
|
creativity=effective_creativity,
|
|
3187
|
-
thinking_budget=thinking_budget if cfg["provider"] in ("google", "openai", "anthropic", "huggingface", "huggingface-together") else None,
|
|
3187
|
+
thinking_budget=thinking_budget if cfg["provider"] in ("google", "openai", "anthropic", "huggingface", "huggingface-together", "ollama") else None,
|
|
3188
3188
|
max_retries=max_retries,
|
|
3189
3189
|
)
|
|
3190
3190
|
|
|
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
|