cat-stack 2.1.0__tar.gz → 2.2.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.1.0 → cat_stack-2.2.0}/PKG-INFO +1 -1
  2. {cat_stack-2.1.0 → cat_stack-2.2.0}/src/catstack/__about__.py +1 -1
  3. {cat_stack-2.1.0 → cat_stack-2.2.0}/src/catstack/_batch.py +1 -1
  4. {cat_stack-2.1.0 → cat_stack-2.2.0}/src/catstack/_providers.py +18 -0
  5. {cat_stack-2.1.0 → cat_stack-2.2.0}/src/catstack/_utils.py +4 -1
  6. {cat_stack-2.1.0 → cat_stack-2.2.0}/src/catstack/calls/stepback.py +31 -0
  7. {cat_stack-2.1.0 → cat_stack-2.2.0}/src/catstack/image_functions.py +2 -0
  8. {cat_stack-2.1.0 → cat_stack-2.2.0}/src/catstack/pdf_functions.py +2 -0
  9. {cat_stack-2.1.0 → cat_stack-2.2.0}/src/catstack/text_functions.py +4 -1
  10. {cat_stack-2.1.0 → cat_stack-2.2.0}/.gitignore +0 -0
  11. {cat_stack-2.1.0 → cat_stack-2.2.0}/LICENSE +0 -0
  12. {cat_stack-2.1.0 → cat_stack-2.2.0}/README.md +0 -0
  13. {cat_stack-2.1.0 → cat_stack-2.2.0}/pyproject.toml +0 -0
  14. {cat_stack-2.1.0 → cat_stack-2.2.0}/src/cat_stack/__init__.py +0 -0
  15. {cat_stack-2.1.0 → cat_stack-2.2.0}/src/catstack/__init__.py +0 -0
  16. {cat_stack-2.1.0 → cat_stack-2.2.0}/src/catstack/_category_analysis.py +0 -0
  17. {cat_stack-2.1.0 → cat_stack-2.2.0}/src/catstack/_chunked.py +0 -0
  18. {cat_stack-2.1.0 → cat_stack-2.2.0}/src/catstack/_embeddings.py +0 -0
  19. {cat_stack-2.1.0 → cat_stack-2.2.0}/src/catstack/_formatter.py +0 -0
  20. {cat_stack-2.1.0 → cat_stack-2.2.0}/src/catstack/_pilot_test.py +0 -0
  21. {cat_stack-2.1.0 → cat_stack-2.2.0}/src/catstack/_prompts.py +0 -0
  22. {cat_stack-2.1.0 → cat_stack-2.2.0}/src/catstack/_review_ui.py +0 -0
  23. {cat_stack-2.1.0 → cat_stack-2.2.0}/src/catstack/_tiebreaker.py +0 -0
  24. {cat_stack-2.1.0 → cat_stack-2.2.0}/src/catstack/_web_fetch.py +0 -0
  25. {cat_stack-2.1.0 → cat_stack-2.2.0}/src/catstack/_wrapper_helpers.py +0 -0
  26. {cat_stack-2.1.0 → cat_stack-2.2.0}/src/catstack/calls/CoVe.py +0 -0
  27. {cat_stack-2.1.0 → cat_stack-2.2.0}/src/catstack/calls/__init__.py +0 -0
  28. {cat_stack-2.1.0 → cat_stack-2.2.0}/src/catstack/calls/image_CoVe.py +0 -0
  29. {cat_stack-2.1.0 → cat_stack-2.2.0}/src/catstack/calls/image_stepback.py +0 -0
  30. {cat_stack-2.1.0 → cat_stack-2.2.0}/src/catstack/calls/pdf_CoVe.py +0 -0
  31. {cat_stack-2.1.0 → cat_stack-2.2.0}/src/catstack/calls/pdf_stepback.py +0 -0
  32. {cat_stack-2.1.0 → cat_stack-2.2.0}/src/catstack/calls/top_n.py +0 -0
  33. {cat_stack-2.1.0 → cat_stack-2.2.0}/src/catstack/classify.py +0 -0
  34. {cat_stack-2.1.0 → cat_stack-2.2.0}/src/catstack/collapse_themes.py +0 -0
  35. {cat_stack-2.1.0 → cat_stack-2.2.0}/src/catstack/explore.py +0 -0
  36. {cat_stack-2.1.0 → cat_stack-2.2.0}/src/catstack/extract.py +0 -0
  37. {cat_stack-2.1.0 → cat_stack-2.2.0}/src/catstack/images/circle.png +0 -0
  38. {cat_stack-2.1.0 → cat_stack-2.2.0}/src/catstack/images/cube.png +0 -0
  39. {cat_stack-2.1.0 → cat_stack-2.2.0}/src/catstack/images/diamond.png +0 -0
  40. {cat_stack-2.1.0 → cat_stack-2.2.0}/src/catstack/images/overlapping_pentagons.png +0 -0
  41. {cat_stack-2.1.0 → cat_stack-2.2.0}/src/catstack/images/rectangles.png +0 -0
  42. {cat_stack-2.1.0 → cat_stack-2.2.0}/src/catstack/model_reference_list.py +0 -0
  43. {cat_stack-2.1.0 → cat_stack-2.2.0}/src/catstack/prompt_tune.py +0 -0
  44. {cat_stack-2.1.0 → cat_stack-2.2.0}/src/catstack/summarize.py +0 -0
  45. {cat_stack-2.1.0 → cat_stack-2.2.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.1.0
3
+ Version: 2.2.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
@@ -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.1.0"
4
+ __version__ = "2.2.0"
5
5
  __author__ = "Chris Soria"
6
6
  __email__ = "chrissoria@berkeley.edu"
7
7
  __title__ = "cat-stack"
@@ -65,7 +65,7 @@ BATCH_ENDPOINTS = {
65
65
  },
66
66
  }
67
67
 
68
- UNSUPPORTED_BATCH_PROVIDERS = {"huggingface", "huggingface-together", "perplexity", "ollama"}
68
+ UNSUPPORTED_BATCH_PROVIDERS = {"huggingface", "huggingface-together", "perplexity", "ollama", "claude-code", "claude-agent"}
69
69
 
70
70
  # Terminal states per provider
71
71
  _TERMINAL_STATES = {
@@ -729,6 +729,24 @@ PROVIDER_CONFIG = {
729
729
  }
730
730
 
731
731
 
732
+ # Providers that route through complete() with no HTTP endpoint of their own
733
+ # (Claude subscription / CLI). Features that build a direct HTTP request
734
+ # (image, PDF) can't use them — guard with a clear error, not a deep crash.
735
+ _SUBSCRIPTION_PROVIDERS = ("claude-code", "claude-agent")
736
+
737
+
738
+ def _require_http_provider(model_source, feature):
739
+ """Raise a clear error when an HTTP-only feature is used with a
740
+ subscription/CLI provider (claude-code / claude-agent)."""
741
+ if model_source in _SUBSCRIPTION_PROVIDERS:
742
+ raise ValueError(
743
+ f"{feature} is not supported with model_source='{model_source}'. "
744
+ "The Claude subscription/CLI backend supports text classification, "
745
+ "extraction, and summarization, but not " + feature.lower() + ". "
746
+ "Use an API-key provider (e.g. model_source='anthropic') instead."
747
+ )
748
+
749
+
732
750
  # =============================================================================
733
751
  # Unified API Client
734
752
  # =============================================================================
@@ -361,7 +361,8 @@ def _get_stepback_insight(model_source, stepback, api_key, user_model, creativit
361
361
  get_stepback_insight_openai,
362
362
  get_stepback_insight_anthropic,
363
363
  get_stepback_insight_google,
364
- get_stepback_insight_mistral
364
+ get_stepback_insight_mistral,
365
+ get_stepback_insight_via_complete
365
366
  )
366
367
 
367
368
  stepback_functions = {
@@ -373,6 +374,8 @@ def _get_stepback_insight(model_source, stepback, api_key, user_model, creativit
373
374
  "anthropic": get_stepback_insight_anthropic,
374
375
  "google": get_stepback_insight_google,
375
376
  "mistral": get_stepback_insight_mistral,
377
+ "claude-code": get_stepback_insight_via_complete,
378
+ "claude-agent": get_stepback_insight_via_complete,
376
379
  }
377
380
 
378
381
  func = stepback_functions.get(model_source)
@@ -187,3 +187,34 @@ def get_stepback_insight_mistral(
187
187
 
188
188
  except Exception as e:
189
189
  return None, False
190
+
191
+
192
+
193
+ def get_stepback_insight_via_complete(
194
+ stepback,
195
+ api_key,
196
+ user_model,
197
+ model_source,
198
+ creativity=None,
199
+ ):
200
+ """Stepback insight via the central UnifiedLLMClient.complete().
201
+
202
+ For providers with no direct HTTP endpoint (claude-agent / claude-code),
203
+ which route through complete() rather than a provider-specific requests.post.
204
+ Returns (insight_text, True) on success, (None, False) otherwise.
205
+ """
206
+ from cat_stack._providers import UnifiedLLMClient
207
+ try:
208
+ client = UnifiedLLMClient(
209
+ provider=model_source, api_key=api_key or "", model=user_model
210
+ )
211
+ insight, error = client.complete(
212
+ messages=[{"role": "user", "content": stepback}],
213
+ creativity=creativity,
214
+ force_json=False, # stepback insight is free text, not JSON
215
+ )
216
+ if error or not insight:
217
+ return None, False
218
+ return insight, True
219
+ except Exception:
220
+ return None, False
@@ -1,6 +1,7 @@
1
1
  import warnings
2
2
 
3
3
  from .text_functions import _detect_model_source
4
+ from ._providers import _require_http_provider
4
5
  from ._utils import _clean_label, _extract_balanced_json
5
6
  from .calls.image_stepback import get_image_stepback_insight
6
7
 
@@ -147,6 +148,7 @@ def image_multi_class(
147
148
  raise FileNotFoundError(f"Directory {save_directory} doesn't exist")
148
149
 
149
150
  model_source = _detect_model_source(user_model, model_source)
151
+ _require_http_provider(model_source, "Image classification")
150
152
 
151
153
  image_files = _load_image_files(image_input)
152
154
 
@@ -1,6 +1,7 @@
1
1
  import warnings
2
2
 
3
3
  from .text_functions import _detect_model_source
4
+ from ._providers import _require_http_provider
4
5
  from ._utils import _clean_label, _extract_balanced_json
5
6
  from .calls.pdf_stepback import get_pdf_stepback_insight
6
7
 
@@ -392,6 +393,7 @@ def pdf_multi_class(
392
393
  raise ValueError(f"mode must be 'image', 'text', or 'both', got: {mode}")
393
394
 
394
395
  model_source = _detect_model_source(user_model, model_source)
396
+ _require_http_provider(model_source, "PDF classification")
395
397
 
396
398
  # Providers with native PDF support (only used in image/both modes)
397
399
  native_pdf_providers = {"anthropic", "google"}
@@ -40,7 +40,8 @@ from .calls.stepback import (
40
40
  get_stepback_insight_openai,
41
41
  get_stepback_insight_anthropic,
42
42
  get_stepback_insight_google,
43
- get_stepback_insight_mistral
43
+ get_stepback_insight_mistral,
44
+ get_stepback_insight_via_complete
44
45
  )
45
46
  from .calls.CoVe import (
46
47
  chain_of_verification_openai,
@@ -90,6 +91,8 @@ def _get_stepback_insight(model_source, stepback, api_key, user_model, creativit
90
91
  "anthropic": get_stepback_insight_anthropic,
91
92
  "google": get_stepback_insight_google,
92
93
  "mistral": get_stepback_insight_mistral,
94
+ "claude-code": get_stepback_insight_via_complete,
95
+ "claude-agent": get_stepback_insight_via_complete,
93
96
  }
94
97
 
95
98
  func = stepback_functions.get(model_source)
File without changes
File without changes
File without changes
File without changes