abstractcore 2.9.1__py3-none-any.whl → 2.11.4__py3-none-any.whl

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 (85) hide show
  1. abstractcore/__init__.py +7 -27
  2. abstractcore/apps/deepsearch.py +9 -4
  3. abstractcore/apps/extractor.py +33 -100
  4. abstractcore/apps/intent.py +19 -0
  5. abstractcore/apps/judge.py +20 -1
  6. abstractcore/apps/summarizer.py +20 -1
  7. abstractcore/architectures/detection.py +34 -1
  8. abstractcore/architectures/response_postprocessing.py +313 -0
  9. abstractcore/assets/architecture_formats.json +38 -8
  10. abstractcore/assets/model_capabilities.json +882 -160
  11. abstractcore/compression/__init__.py +1 -2
  12. abstractcore/compression/glyph_processor.py +6 -4
  13. abstractcore/config/main.py +52 -20
  14. abstractcore/config/manager.py +390 -12
  15. abstractcore/config/vision_config.py +5 -5
  16. abstractcore/core/interface.py +151 -3
  17. abstractcore/core/session.py +16 -10
  18. abstractcore/download.py +1 -1
  19. abstractcore/embeddings/manager.py +20 -6
  20. abstractcore/endpoint/__init__.py +2 -0
  21. abstractcore/endpoint/app.py +458 -0
  22. abstractcore/mcp/client.py +3 -1
  23. abstractcore/media/__init__.py +52 -17
  24. abstractcore/media/auto_handler.py +42 -22
  25. abstractcore/media/base.py +44 -1
  26. abstractcore/media/capabilities.py +12 -33
  27. abstractcore/media/enrichment.py +105 -0
  28. abstractcore/media/handlers/anthropic_handler.py +19 -28
  29. abstractcore/media/handlers/local_handler.py +124 -70
  30. abstractcore/media/handlers/openai_handler.py +19 -31
  31. abstractcore/media/processors/__init__.py +4 -2
  32. abstractcore/media/processors/audio_processor.py +57 -0
  33. abstractcore/media/processors/office_processor.py +8 -3
  34. abstractcore/media/processors/pdf_processor.py +46 -3
  35. abstractcore/media/processors/text_processor.py +22 -24
  36. abstractcore/media/processors/video_processor.py +58 -0
  37. abstractcore/media/types.py +97 -4
  38. abstractcore/media/utils/image_scaler.py +20 -2
  39. abstractcore/media/utils/video_frames.py +219 -0
  40. abstractcore/media/vision_fallback.py +136 -22
  41. abstractcore/processing/__init__.py +32 -3
  42. abstractcore/processing/basic_deepsearch.py +15 -10
  43. abstractcore/processing/basic_intent.py +3 -2
  44. abstractcore/processing/basic_judge.py +3 -2
  45. abstractcore/processing/basic_summarizer.py +1 -1
  46. abstractcore/providers/__init__.py +3 -1
  47. abstractcore/providers/anthropic_provider.py +95 -8
  48. abstractcore/providers/base.py +1516 -81
  49. abstractcore/providers/huggingface_provider.py +546 -69
  50. abstractcore/providers/lmstudio_provider.py +30 -916
  51. abstractcore/providers/mlx_provider.py +382 -35
  52. abstractcore/providers/model_capabilities.py +5 -1
  53. abstractcore/providers/ollama_provider.py +99 -15
  54. abstractcore/providers/openai_compatible_provider.py +406 -180
  55. abstractcore/providers/openai_provider.py +188 -44
  56. abstractcore/providers/openrouter_provider.py +76 -0
  57. abstractcore/providers/registry.py +61 -5
  58. abstractcore/providers/streaming.py +138 -33
  59. abstractcore/providers/vllm_provider.py +92 -817
  60. abstractcore/server/app.py +478 -28
  61. abstractcore/server/audio_endpoints.py +139 -0
  62. abstractcore/server/vision_endpoints.py +1319 -0
  63. abstractcore/structured/handler.py +316 -41
  64. abstractcore/tools/common_tools.py +5501 -2012
  65. abstractcore/tools/comms_tools.py +1641 -0
  66. abstractcore/tools/core.py +37 -7
  67. abstractcore/tools/handler.py +4 -9
  68. abstractcore/tools/parser.py +49 -2
  69. abstractcore/tools/tag_rewriter.py +2 -1
  70. abstractcore/tools/telegram_tdlib.py +407 -0
  71. abstractcore/tools/telegram_tools.py +261 -0
  72. abstractcore/utils/cli.py +1085 -72
  73. abstractcore/utils/structured_logging.py +29 -8
  74. abstractcore/utils/token_utils.py +2 -0
  75. abstractcore/utils/truncation.py +29 -0
  76. abstractcore/utils/version.py +3 -4
  77. abstractcore/utils/vlm_token_calculator.py +12 -2
  78. abstractcore-2.11.4.dist-info/METADATA +562 -0
  79. abstractcore-2.11.4.dist-info/RECORD +133 -0
  80. {abstractcore-2.9.1.dist-info → abstractcore-2.11.4.dist-info}/WHEEL +1 -1
  81. {abstractcore-2.9.1.dist-info → abstractcore-2.11.4.dist-info}/entry_points.txt +1 -0
  82. abstractcore-2.9.1.dist-info/METADATA +0 -1190
  83. abstractcore-2.9.1.dist-info/RECORD +0 -119
  84. {abstractcore-2.9.1.dist-info → abstractcore-2.11.4.dist-info}/licenses/LICENSE +0 -0
  85. {abstractcore-2.9.1.dist-info → abstractcore-2.11.4.dist-info}/top_level.txt +0 -0
@@ -57,7 +57,7 @@ def _get_config_defaults():
57
57
  "NONE": logging.CRITICAL + 10 # Higher than CRITICAL to effectively disable logging
58
58
  }
59
59
 
60
- console_level = level_map.get(logging_config.console_level, logging.WARNING)
60
+ console_level = level_map.get(logging_config.console_level, logging.ERROR)
61
61
  file_level = level_map.get(logging_config.file_level, logging.DEBUG)
62
62
 
63
63
  # Use log_base_dir if file logging enabled
@@ -66,6 +66,27 @@ def _get_config_defaults():
66
66
  # Expand user home directory
67
67
  log_dir = str(Path(logging_config.log_base_dir).expanduser())
68
68
 
69
+ # Environment overrides (optional)
70
+ env_console = os.getenv("ABSTRACTCORE_CONSOLE_LOG_LEVEL")
71
+ if isinstance(env_console, str) and env_console.strip():
72
+ env_level = env_console.strip().upper()
73
+ if env_level == "NONE":
74
+ console_level = None
75
+ else:
76
+ console_level = level_map.get(env_level, console_level)
77
+
78
+ env_file = os.getenv("ABSTRACTCORE_FILE_LOG_LEVEL")
79
+ if isinstance(env_file, str) and env_file.strip():
80
+ env_level = env_file.strip().upper()
81
+ if env_level == "NONE":
82
+ file_level = None
83
+ else:
84
+ file_level = level_map.get(env_level, file_level)
85
+
86
+ env_log_dir = os.getenv("ABSTRACTCORE_LOG_BASE_DIR")
87
+ if isinstance(env_log_dir, str) and env_log_dir.strip():
88
+ log_dir = str(Path(env_log_dir.strip()).expanduser())
89
+
69
90
  return {
70
91
  'console_level': console_level,
71
92
  'file_level': file_level,
@@ -77,7 +98,7 @@ def _get_config_defaults():
77
98
  except Exception:
78
99
  # Fallback to hardcoded defaults if config unavailable
79
100
  return {
80
- 'console_level': logging.WARNING,
101
+ 'console_level': logging.ERROR,
81
102
  'file_level': logging.DEBUG,
82
103
  'log_dir': None,
83
104
  'verbatim_enabled': True,
@@ -89,7 +110,7 @@ def _get_config_defaults():
89
110
  LOG_LEVEL_COLORS = {
90
111
  'DEBUG': Fore.CYAN + Style.DIM, # Cyan, dimmed (less prominent)
91
112
  'INFO': Fore.GREEN, # Green (informational, good)
92
- 'WARNING': Fore.YELLOW + Style.BRIGHT, # Bright yellow (attention)
113
+ 'WARNING': "\033[38;5;214m" + Style.BRIGHT, # Orange (attention)
93
114
  'ERROR': Fore.RED, # Red (error)
94
115
  'CRITICAL': Fore.RED + Style.BRIGHT # Bright red (critical)
95
116
  }
@@ -215,7 +236,7 @@ class LogConfig:
215
236
  root_logger.handlers.clear()
216
237
 
217
238
  # Console handler
218
- if self.console_level is not None:
239
+ if self.console_level is not None and self.console_level < (logging.CRITICAL + 10):
219
240
  console_handler = logging.StreamHandler(sys.stdout)
220
241
  console_handler.setLevel(self.console_level)
221
242
 
@@ -260,8 +281,8 @@ class LogConfig:
260
281
  if effective_levels:
261
282
  root_logger.setLevel(min(effective_levels))
262
283
  else:
263
- # No handlers enabled, set to WARNING as a safe default
264
- root_logger.setLevel(logging.WARNING)
284
+ # No handlers enabled, set to ERROR as a safe default
285
+ root_logger.setLevel(logging.ERROR)
265
286
 
266
287
 
267
288
  # Global config instance
@@ -493,7 +514,7 @@ def get_logger(name: str) -> StructuredLogger:
493
514
 
494
515
 
495
516
  def configure_logging(
496
- console_level: Optional[int] = logging.WARNING,
517
+ console_level: Optional[int] = logging.ERROR,
497
518
  file_level: Optional[int] = logging.DEBUG,
498
519
  log_dir: Optional[str] = None,
499
520
  verbatim_enabled: bool = True,
@@ -577,4 +598,4 @@ def suppress_stdout_stderr():
577
598
  yield
578
599
  finally:
579
600
  sys.stdout = old_stdout
580
- sys.stderr = old_stderr
601
+ sys.stderr = old_stderr
@@ -230,12 +230,14 @@ class TokenUtils:
230
230
  return ContentType.NATURAL_LANGUAGE
231
231
 
232
232
  # Sample first 1000 chars for efficiency
233
+ #[WARNING:TRUNCATION] bounded sample for heuristic detection (performance)
233
234
  sample = text[:1000]
234
235
 
235
236
  # JSON detection
236
237
  if sample.strip().startswith(('{', '[')):
237
238
  try:
238
239
  import json
240
+ #[WARNING:TRUNCATION] bounded JSON probe for heuristic detection (performance)
239
241
  json.loads(sample[:500]) # Try to parse a portion
240
242
  return ContentType.JSON
241
243
  except:
@@ -0,0 +1,29 @@
1
+ """Truncation utilities (explicit + searchable).
2
+
3
+ Policy authority: ADR-0026 (docs/adr/0026-truncation-policy-and-contract.md).
4
+
5
+ All lossy truncation must:
6
+ - be explicit in the returned text (marker),
7
+ - and be searchable in code via `#[WARNING:TRUNCATION]`.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from typing import Any
13
+
14
+
15
+ def preview_text(value: Any, *, max_chars: int, marker: str = "… (truncated)") -> str:
16
+ """Return `value` as a bounded preview with an explicit truncation marker."""
17
+ s = str(value or "")
18
+ if max_chars <= 0:
19
+ #[WARNING:TRUNCATION] bounded preview requested with max_chars<=0
20
+ return ""
21
+ max_chars_i = int(max_chars)
22
+ if len(s) <= max_chars_i:
23
+ return s
24
+ #[WARNING:TRUNCATION] bounded preview (logs/telemetry/UI)
25
+ keep = max(0, max_chars_i - len(marker))
26
+ if keep <= 0:
27
+ return marker[:max_chars_i].rstrip()
28
+ return s[:keep].rstrip() + marker
29
+
@@ -2,13 +2,12 @@
2
2
  Version management for AbstractCore.
3
3
 
4
4
  This module provides the package version as a static constant that serves as the
5
- single source of truth for the Python code. The version is also maintained in
6
- pyproject.toml for packaging, requiring manual synchronization during releases.
5
+ single source of truth. Packaging reads the version from this module via
6
+ `[tool.setuptools.dynamic]` in `pyproject.toml`.
7
7
 
8
8
  This approach ensures reliable version access in all deployment scenarios,
9
9
  including when the package is installed from PyPI where pyproject.toml is not available.
10
10
  """
11
11
 
12
12
  # Package version - update this when releasing new versions
13
- # This must be manually synchronized with the version in pyproject.toml
14
- __version__ = "2.9.1"
13
+ __version__ = "2.11.4"
@@ -24,7 +24,12 @@ from typing import Tuple, Dict, Any, Optional, List
24
24
  from pathlib import Path
25
25
  import logging
26
26
 
27
- from PIL import Image
27
+ try:
28
+ from PIL import Image
29
+ PIL_AVAILABLE = True
30
+ except ImportError: # pragma: no cover
31
+ Image = None
32
+ PIL_AVAILABLE = False
28
33
 
29
34
  from ..utils.structured_logging import get_logger
30
35
  from ..architectures.detection import get_model_capabilities, detect_architecture
@@ -143,6 +148,11 @@ class VLMTokenCalculator:
143
148
  """
144
149
  # Get image dimensions
145
150
  if image_path and image_path.exists():
151
+ if not PIL_AVAILABLE:
152
+ raise ImportError(
153
+ "PIL/Pillow is required to read image files for token calculation. "
154
+ "Install with: pip install \"abstractcore[media]\""
155
+ )
146
156
  try:
147
157
  with Image.open(image_path) as img:
148
158
  width, height = img.size
@@ -653,4 +663,4 @@ def calculate_glyph_compression_ratio(original_tokens: int,
653
663
  model: str = '') -> Dict[str, Any]:
654
664
  """Calculate accurate Glyph compression ratio."""
655
665
  calculator = VLMTokenCalculator()
656
- return calculator.get_compression_ratio(original_tokens, image_paths, provider, model)
666
+ return calculator.get_compression_ratio(original_tokens, image_paths, provider, model)