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.
Files changed (45) hide show
  1. {cat_stack-2.2.0 → cat_stack-2.3.0}/PKG-INFO +2 -2
  2. {cat_stack-2.2.0 → cat_stack-2.3.0}/pyproject.toml +1 -1
  3. {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/__about__.py +1 -1
  4. {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/image_functions.py +31 -1
  5. {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/pdf_functions.py +35 -1
  6. {cat_stack-2.2.0 → cat_stack-2.3.0}/.gitignore +0 -0
  7. {cat_stack-2.2.0 → cat_stack-2.3.0}/LICENSE +0 -0
  8. {cat_stack-2.2.0 → cat_stack-2.3.0}/README.md +0 -0
  9. {cat_stack-2.2.0 → cat_stack-2.3.0}/src/cat_stack/__init__.py +0 -0
  10. {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/__init__.py +0 -0
  11. {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/_batch.py +0 -0
  12. {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/_category_analysis.py +0 -0
  13. {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/_chunked.py +0 -0
  14. {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/_embeddings.py +0 -0
  15. {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/_formatter.py +0 -0
  16. {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/_pilot_test.py +0 -0
  17. {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/_prompts.py +0 -0
  18. {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/_providers.py +0 -0
  19. {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/_review_ui.py +0 -0
  20. {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/_tiebreaker.py +0 -0
  21. {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/_utils.py +0 -0
  22. {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/_web_fetch.py +0 -0
  23. {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/_wrapper_helpers.py +0 -0
  24. {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/calls/CoVe.py +0 -0
  25. {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/calls/__init__.py +0 -0
  26. {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/calls/image_CoVe.py +0 -0
  27. {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/calls/image_stepback.py +0 -0
  28. {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/calls/pdf_CoVe.py +0 -0
  29. {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/calls/pdf_stepback.py +0 -0
  30. {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/calls/stepback.py +0 -0
  31. {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/calls/top_n.py +0 -0
  32. {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/classify.py +0 -0
  33. {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/collapse_themes.py +0 -0
  34. {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/explore.py +0 -0
  35. {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/extract.py +0 -0
  36. {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/images/circle.png +0 -0
  37. {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/images/cube.png +0 -0
  38. {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/images/diamond.png +0 -0
  39. {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/images/overlapping_pentagons.png +0 -0
  40. {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/images/rectangles.png +0 -0
  41. {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/model_reference_list.py +0 -0
  42. {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/prompt_tune.py +0 -0
  43. {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/summarize.py +0 -0
  44. {cat_stack-2.2.0 → cat_stack-2.3.0}/src/catstack/text_functions.py +0 -0
  45. {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.2.0
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.1.0; extra == 'agent'
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.1.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.2.0"
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
- _require_http_provider(model_source, "Image classification")
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
- _require_http_provider(model_source, "PDF classification")
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