cat-stack 2.2.0__tar.gz → 2.3.0__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-2.2.0 → cat_stack-2.3.0}/PKG-INFO +2 -2
- {cat_stack-2.2.0 → cat_stack-2.3.0}/pyproject.toml +1 -1
- {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/__about__.py +1 -1
- {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/image_functions.py +31 -1
- {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/pdf_functions.py +35 -1
- {cat_stack-2.2.0 → cat_stack-2.3.0}/.gitignore +0 -0
- {cat_stack-2.2.0 → cat_stack-2.3.0}/LICENSE +0 -0
- {cat_stack-2.2.0 → cat_stack-2.3.0}/README.md +0 -0
- {cat_stack-2.2.0 → cat_stack-2.3.0}/src/cat_stack/__init__.py +0 -0
- {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/__init__.py +0 -0
- {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/_batch.py +0 -0
- {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/_category_analysis.py +0 -0
- {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/_chunked.py +0 -0
- {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/_embeddings.py +0 -0
- {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/_formatter.py +0 -0
- {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/_pilot_test.py +0 -0
- {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/_prompts.py +0 -0
- {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/_providers.py +0 -0
- {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/_review_ui.py +0 -0
- {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/_tiebreaker.py +0 -0
- {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/_utils.py +0 -0
- {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/_web_fetch.py +0 -0
- {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/_wrapper_helpers.py +0 -0
- {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/calls/CoVe.py +0 -0
- {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/calls/__init__.py +0 -0
- {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/calls/image_CoVe.py +0 -0
- {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/calls/image_stepback.py +0 -0
- {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/calls/pdf_CoVe.py +0 -0
- {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/calls/pdf_stepback.py +0 -0
- {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/calls/stepback.py +0 -0
- {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/calls/top_n.py +0 -0
- {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/classify.py +0 -0
- {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/collapse_themes.py +0 -0
- {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/explore.py +0 -0
- {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/extract.py +0 -0
- {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/images/circle.png +0 -0
- {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/images/cube.png +0 -0
- {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/images/diamond.png +0 -0
- {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/images/overlapping_pentagons.png +0 -0
- {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/images/rectangles.png +0 -0
- {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/model_reference_list.py +0 -0
- {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/prompt_tune.py +0 -0
- {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/summarize.py +0 -0
- {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/text_functions.py +0 -0
- {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/text_functions_ensemble.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cat-stack
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
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
|
|
@@ -23,7 +23,7 @@ Requires-Dist: pandas
|
|
|
23
23
|
Requires-Dist: requests
|
|
24
24
|
Requires-Dist: tqdm
|
|
25
25
|
Provides-Extra: agent
|
|
26
|
-
Requires-Dist: cat-claws>=0.
|
|
26
|
+
Requires-Dist: cat-claws>=0.2.0; extra == 'agent'
|
|
27
27
|
Provides-Extra: docx
|
|
28
28
|
Requires-Dist: python-docx>=1.0.0; extra == 'docx'
|
|
29
29
|
Provides-Extra: embeddings
|
|
@@ -35,7 +35,7 @@ pdf = ["PyMuPDF>=1.23.0"]
|
|
|
35
35
|
docx = ["python-docx>=1.0.0"]
|
|
36
36
|
formatter = ["torch>=2.0.0", "transformers>=4.40.0", "accelerate>=0.27.0"]
|
|
37
37
|
embeddings = ["sentence-transformers>=2.2.0"]
|
|
38
|
-
agent = ["cat-claws>=0.
|
|
38
|
+
agent = ["cat-claws>=0.2.0"]
|
|
39
39
|
|
|
40
40
|
[project.urls]
|
|
41
41
|
Documentation = "https://github.com/chrissoria/cat-stack#readme"
|
|
@@ -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__ = "2.
|
|
4
|
+
__version__ = "2.3.0"
|
|
5
5
|
__author__ = "Chris Soria"
|
|
6
6
|
__email__ = "chrissoria@berkeley.edu"
|
|
7
7
|
__title__ = "cat-stack"
|
|
@@ -148,7 +148,12 @@ def image_multi_class(
|
|
|
148
148
|
raise FileNotFoundError(f"Directory {save_directory} doesn't exist")
|
|
149
149
|
|
|
150
150
|
model_source = _detect_model_source(user_model, model_source)
|
|
151
|
-
|
|
151
|
+
if model_source == "claude-code":
|
|
152
|
+
raise ValueError(
|
|
153
|
+
"Image classification is not supported with model_source='claude-code' "
|
|
154
|
+
"(the text-only CLI shim). Use model_source='claude-agent' (the cat-claws "
|
|
155
|
+
"subscription backend) or an API-key provider."
|
|
156
|
+
)
|
|
152
157
|
|
|
153
158
|
image_files = _load_image_files(image_input)
|
|
154
159
|
|
|
@@ -663,6 +668,27 @@ Provide the final categorization in the same JSON format:"""
|
|
|
663
668
|
|
|
664
669
|
return """{"1":"e"}""", "Max retries exceeded"
|
|
665
670
|
|
|
671
|
+
def _call_claude_agent_image(base_text, encoded, media_type):
|
|
672
|
+
"""Image classification via the cat-claws multimodal adapter (Claude
|
|
673
|
+
subscription, no API key). Returns (reply, error) like _call_anthropic."""
|
|
674
|
+
try:
|
|
675
|
+
from catclaws._adapters import get_adapter
|
|
676
|
+
except ImportError:
|
|
677
|
+
return None, ("cat-claws is not installed. Run: pip install cat-stack[agent]")
|
|
678
|
+
import asyncio
|
|
679
|
+
adapter = get_adapter("claude")
|
|
680
|
+
_system = ("You are an image classification engine. Follow the user's "
|
|
681
|
+
"instructions exactly and reply with only what they ask for.")
|
|
682
|
+
try:
|
|
683
|
+
reply, error = asyncio.run(adapter.one_shot(
|
|
684
|
+
base_text, system_prompt=_system, model=user_model,
|
|
685
|
+
thinking_budget=thinking_budget or 0,
|
|
686
|
+
images=[{"media_type": media_type, "data": encoded}],
|
|
687
|
+
))
|
|
688
|
+
return (None, error) if error else (reply, None)
|
|
689
|
+
except Exception as e:
|
|
690
|
+
return None, f"cat-claws image call failed: {e}"
|
|
691
|
+
|
|
666
692
|
def _process_single_image(img_path):
|
|
667
693
|
"""Process a single image and return (reply, error_msg)."""
|
|
668
694
|
encoded, ext, is_valid = _encode_image(img_path)
|
|
@@ -703,6 +729,10 @@ Provide the final categorization in the same JSON format:"""
|
|
|
703
729
|
image_content = {"type": "image_url", "image_url": {"url": encoded_image, "detail": "high"}}
|
|
704
730
|
return _call_mistral(prompt, step2_prompt, step3_prompt, step4_prompt, image_content)
|
|
705
731
|
|
|
732
|
+
elif model_source == "claude-agent":
|
|
733
|
+
media_type = f"image/{ext}" if ext else "image/jpeg"
|
|
734
|
+
return _call_claude_agent_image(base_prompt_text, encoded, media_type)
|
|
735
|
+
|
|
706
736
|
else:
|
|
707
737
|
raise ValueError("Unknown source! Choose from OpenAI, Anthropic, Perplexity, Google, xAI, Huggingface, or Mistral")
|
|
708
738
|
|
|
@@ -393,7 +393,12 @@ def pdf_multi_class(
|
|
|
393
393
|
raise ValueError(f"mode must be 'image', 'text', or 'both', got: {mode}")
|
|
394
394
|
|
|
395
395
|
model_source = _detect_model_source(user_model, model_source)
|
|
396
|
-
|
|
396
|
+
if model_source == "claude-code":
|
|
397
|
+
raise ValueError(
|
|
398
|
+
"PDF classification is not supported with model_source='claude-code' "
|
|
399
|
+
"(the text-only CLI shim). Use model_source='claude-agent' (the cat-claws "
|
|
400
|
+
"subscription backend) or an API-key provider."
|
|
401
|
+
)
|
|
397
402
|
|
|
398
403
|
# Providers with native PDF support (only used in image/both modes)
|
|
399
404
|
native_pdf_providers = {"anthropic", "google"}
|
|
@@ -1181,6 +1186,28 @@ Provide the final categorization in the same JSON format:"""
|
|
|
1181
1186
|
|
|
1182
1187
|
return """{"1":"e"}""", "Max retries exceeded"
|
|
1183
1188
|
|
|
1189
|
+
def _call_claude_agent_pdf(base_text, encoded_image):
|
|
1190
|
+
"""PDF-page classification via the cat-claws multimodal adapter. The page
|
|
1191
|
+
is rendered to an image (PDF-as-images); Claude subscription, no API key.
|
|
1192
|
+
Returns (reply, error)."""
|
|
1193
|
+
try:
|
|
1194
|
+
from catclaws._adapters import get_adapter
|
|
1195
|
+
except ImportError:
|
|
1196
|
+
return None, ("cat-claws is not installed. Run: pip install cat-stack[agent]")
|
|
1197
|
+
import asyncio
|
|
1198
|
+
adapter = get_adapter("claude")
|
|
1199
|
+
_system = ("You are a document-page classification engine. Follow the "
|
|
1200
|
+
"user's instructions exactly and reply with only what they ask for.")
|
|
1201
|
+
try:
|
|
1202
|
+
reply, error = asyncio.run(adapter.one_shot(
|
|
1203
|
+
base_text, system_prompt=_system, model=user_model,
|
|
1204
|
+
thinking_budget=thinking_budget or 0,
|
|
1205
|
+
images=[{"media_type": "image/png", "data": encoded_image}],
|
|
1206
|
+
))
|
|
1207
|
+
return (None, error) if error else (reply, None)
|
|
1208
|
+
except Exception as e:
|
|
1209
|
+
return None, f"cat-claws PDF call failed: {e}"
|
|
1210
|
+
|
|
1184
1211
|
def _process_single_page(pdf_path, page_index, page_label):
|
|
1185
1212
|
"""Process a single PDF page and return (reply, error_msg)."""
|
|
1186
1213
|
|
|
@@ -1260,6 +1287,13 @@ Provide the final categorization in the same JSON format:"""
|
|
|
1260
1287
|
prompt_data = _build_prompt_google_pdf(encoded_pdf, base_prompt_text)
|
|
1261
1288
|
return _call_google(prompt_data, step2_prompt, step3_prompt, step4_prompt, base_prompt_text)
|
|
1262
1289
|
|
|
1290
|
+
elif model_source == "claude-agent":
|
|
1291
|
+
image_bytes, is_valid = _extract_page_as_image_bytes(pdf_path, page_index)
|
|
1292
|
+
if not is_valid:
|
|
1293
|
+
return None, "Failed to render PDF page to image"
|
|
1294
|
+
encoded_image = _encode_bytes_to_base64(image_bytes)
|
|
1295
|
+
return _call_claude_agent_pdf(base_prompt_text, encoded_image)
|
|
1296
|
+
|
|
1263
1297
|
# Handle providers requiring image conversion
|
|
1264
1298
|
else:
|
|
1265
1299
|
image_bytes, is_valid = _extract_page_as_image_bytes(pdf_path, page_index)
|
|
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
|