abstractcore 2.4.2__py3-none-any.whl → 2.4.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 (34) hide show
  1. abstractcore/apps/app_config_utils.py +19 -0
  2. abstractcore/apps/summarizer.py +85 -56
  3. abstractcore/architectures/detection.py +15 -4
  4. abstractcore/assets/architecture_formats.json +1 -1
  5. abstractcore/assets/model_capabilities.json +420 -11
  6. abstractcore/core/interface.py +2 -0
  7. abstractcore/core/session.py +4 -0
  8. abstractcore/embeddings/manager.py +54 -16
  9. abstractcore/media/__init__.py +116 -148
  10. abstractcore/media/auto_handler.py +363 -0
  11. abstractcore/media/base.py +456 -0
  12. abstractcore/media/capabilities.py +335 -0
  13. abstractcore/media/types.py +300 -0
  14. abstractcore/media/vision_fallback.py +260 -0
  15. abstractcore/providers/anthropic_provider.py +18 -1
  16. abstractcore/providers/base.py +187 -0
  17. abstractcore/providers/huggingface_provider.py +111 -12
  18. abstractcore/providers/lmstudio_provider.py +88 -5
  19. abstractcore/providers/mlx_provider.py +33 -1
  20. abstractcore/providers/ollama_provider.py +37 -3
  21. abstractcore/providers/openai_provider.py +18 -1
  22. abstractcore/server/app.py +1390 -104
  23. abstractcore/tools/common_tools.py +12 -8
  24. abstractcore/utils/__init__.py +9 -5
  25. abstractcore/utils/cli.py +199 -17
  26. abstractcore/utils/message_preprocessor.py +182 -0
  27. abstractcore/utils/structured_logging.py +117 -16
  28. abstractcore/utils/version.py +1 -1
  29. {abstractcore-2.4.2.dist-info → abstractcore-2.4.4.dist-info}/METADATA +214 -20
  30. {abstractcore-2.4.2.dist-info → abstractcore-2.4.4.dist-info}/RECORD +34 -27
  31. {abstractcore-2.4.2.dist-info → abstractcore-2.4.4.dist-info}/entry_points.txt +1 -0
  32. {abstractcore-2.4.2.dist-info → abstractcore-2.4.4.dist-info}/WHEEL +0 -0
  33. {abstractcore-2.4.2.dist-info → abstractcore-2.4.4.dist-info}/licenses/LICENSE +0 -0
  34. {abstractcore-2.4.2.dist-info → abstractcore-2.4.4.dist-info}/top_level.txt +0 -0
@@ -25,6 +25,94 @@ try:
25
25
  except ImportError:
26
26
  STRUCTLOG_AVAILABLE = False
27
27
 
28
+ try:
29
+ import colorama
30
+ from colorama import Fore, Style
31
+ COLORAMA_AVAILABLE = True
32
+ # Initialize colorama for cross-platform colored output
33
+ colorama.init(autoreset=True)
34
+ except ImportError:
35
+ COLORAMA_AVAILABLE = False
36
+ # Fallback empty classes if colorama not available
37
+ class Fore:
38
+ RED = YELLOW = GREEN = CYAN = BLUE = MAGENTA = WHITE = RESET = ""
39
+ class Style:
40
+ BRIGHT = DIM = RESET_ALL = ""
41
+
42
+ # Import configuration manager
43
+ def _get_config_defaults():
44
+ """Get configuration defaults from centralized config system."""
45
+ try:
46
+ from ..config import get_config_manager
47
+ config_manager = get_config_manager()
48
+ logging_config = config_manager.config.logging
49
+
50
+ # Convert string levels to logging constants
51
+ level_map = {
52
+ "DEBUG": logging.DEBUG,
53
+ "INFO": logging.INFO,
54
+ "WARNING": logging.WARNING,
55
+ "ERROR": logging.ERROR,
56
+ "CRITICAL": logging.CRITICAL,
57
+ "NONE": logging.CRITICAL + 10 # Higher than CRITICAL to effectively disable logging
58
+ }
59
+
60
+ console_level = level_map.get(logging_config.console_level, logging.WARNING)
61
+ file_level = level_map.get(logging_config.file_level, logging.DEBUG)
62
+
63
+ # Use log_base_dir if file logging enabled
64
+ log_dir = None
65
+ if logging_config.file_logging_enabled and logging_config.log_base_dir:
66
+ # Expand user home directory
67
+ log_dir = str(Path(logging_config.log_base_dir).expanduser())
68
+
69
+ return {
70
+ 'console_level': console_level,
71
+ 'file_level': file_level,
72
+ 'log_dir': log_dir,
73
+ 'verbatim_enabled': logging_config.verbatim_enabled,
74
+ 'console_json': logging_config.console_json,
75
+ 'file_json': logging_config.file_json
76
+ }
77
+ except Exception:
78
+ # Fallback to hardcoded defaults if config unavailable
79
+ return {
80
+ 'console_level': logging.WARNING,
81
+ 'file_level': logging.DEBUG,
82
+ 'log_dir': None,
83
+ 'verbatim_enabled': True,
84
+ 'console_json': False,
85
+ 'file_json': True
86
+ }
87
+
88
+ # Color mapping for log levels
89
+ LOG_LEVEL_COLORS = {
90
+ 'DEBUG': Fore.CYAN + Style.DIM, # Cyan, dimmed (less prominent)
91
+ 'INFO': Fore.GREEN, # Green (informational, good)
92
+ 'WARNING': Fore.YELLOW + Style.BRIGHT, # Bright yellow (attention)
93
+ 'ERROR': Fore.RED, # Red (error)
94
+ 'CRITICAL': Fore.RED + Style.BRIGHT # Bright red (critical)
95
+ }
96
+
97
+ class ColoredFormatter(logging.Formatter):
98
+ """Custom formatter that adds colors to log levels."""
99
+
100
+ def format(self, record):
101
+ # Get the original formatted message
102
+ formatted_message = super().format(record)
103
+
104
+ # Only add colors if colorama is available and we're outputting to a terminal
105
+ if COLORAMA_AVAILABLE and hasattr(sys.stdout, 'isatty') and sys.stdout.isatty():
106
+ # Get color for this level
107
+ level_color = LOG_LEVEL_COLORS.get(record.levelname, '')
108
+
109
+ if level_color:
110
+ # Replace the level name with colored version
111
+ colored_level = f"{level_color}[{record.levelname}]{Style.RESET_ALL}"
112
+ formatted_message = formatted_message.replace(f"[{record.levelname}]", colored_level)
113
+
114
+ return formatted_message
115
+
28
116
  # Global configuration
29
117
  class LogConfig:
30
118
  """Global logging configuration singleton."""
@@ -40,15 +128,19 @@ class LogConfig:
40
128
  if self._initialized:
41
129
  return
42
130
 
43
- # Default configuration
44
- self.console_level = logging.WARNING
45
- self.file_level = logging.DEBUG
46
- self.log_dir = None
47
- self.verbatim_enabled = True
48
- self.console_json = False
49
- self.file_json = True
131
+ # Load configuration from centralized config system
132
+ config_defaults = _get_config_defaults()
133
+ self.console_level = config_defaults['console_level']
134
+ self.file_level = config_defaults['file_level']
135
+ self.log_dir = config_defaults['log_dir']
136
+ self.verbatim_enabled = config_defaults['verbatim_enabled']
137
+ self.console_json = config_defaults['console_json']
138
+ self.file_json = config_defaults['file_json']
50
139
  self._initialized = True
51
140
 
141
+ # Setup logging with configuration
142
+ self._setup_structlog()
143
+
52
144
  def configure(self,
53
145
  console_level: Optional[int] = None,
54
146
  file_level: Optional[int] = None,
@@ -130,7 +222,8 @@ class LogConfig:
130
222
  if self.console_json:
131
223
  console_formatter = logging.Formatter('%(message)s')
132
224
  else:
133
- console_formatter = logging.Formatter(
225
+ # Use colored formatter for better visual distinction
226
+ console_formatter = ColoredFormatter(
134
227
  '%(asctime)s [%(levelname)s] %(name)s: %(message)s',
135
228
  datefmt='%H:%M:%S'
136
229
  )
@@ -140,7 +233,9 @@ class LogConfig:
140
233
  # File handler
141
234
  if self.log_dir and self.file_level is not None:
142
235
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
143
- log_file = Path(self.log_dir) / f"abstractcore_{timestamp}.log"
236
+ log_dir_expanded = Path(self.log_dir).expanduser()
237
+ log_dir_expanded.mkdir(parents=True, exist_ok=True)
238
+ log_file = log_dir_expanded / f"abstractcore_{timestamp}.log"
144
239
 
145
240
  file_handler = logging.FileHandler(log_file)
146
241
  file_handler.setLevel(self.file_level)
@@ -154,13 +249,19 @@ class LogConfig:
154
249
  file_handler.setFormatter(file_formatter)
155
250
  root_logger.addHandler(file_handler)
156
251
 
157
- # Set root logger level to the most verbose level
158
- if self.console_level is not None and self.file_level is not None:
159
- root_logger.setLevel(min(self.console_level, self.file_level))
160
- elif self.console_level is not None:
161
- root_logger.setLevel(self.console_level)
162
- elif self.file_level is not None:
163
- root_logger.setLevel(self.file_level)
252
+ # Set root logger level to the most verbose level of enabled handlers
253
+ # Only consider file_level if file logging is actually enabled (log_dir is set)
254
+ effective_levels = []
255
+ if self.console_level is not None:
256
+ effective_levels.append(self.console_level)
257
+ if self.file_level is not None and self.log_dir:
258
+ effective_levels.append(self.file_level)
259
+
260
+ if effective_levels:
261
+ root_logger.setLevel(min(effective_levels))
262
+ else:
263
+ # No handlers enabled, set to WARNING as a safe default
264
+ root_logger.setLevel(logging.WARNING)
164
265
 
165
266
 
166
267
  # Global config instance
@@ -11,4 +11,4 @@ including when the package is installed from PyPI where pyproject.toml is not av
11
11
 
12
12
  # Package version - update this when releasing new versions
13
13
  # This must be manually synchronized with the version in pyproject.toml
14
- __version__ = "2.4.2"
14
+ __version__ = "2.4.4"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: abstractcore
3
- Version: 2.4.2
3
+ Version: 2.4.4
4
4
  Summary: Unified interface to all LLM providers with essential infrastructure for tool calling, streaming, and model management
5
5
  Author-email: Laurent-Philippe Albou <contact@abstractcore.ai>
6
6
  Maintainer-email: Laurent-Philippe Albou <contact@abstractcore.ai>
@@ -46,6 +46,11 @@ Provides-Extra: embeddings
46
46
  Requires-Dist: sentence-transformers<4.0.0,>=2.7.0; extra == "embeddings"
47
47
  Requires-Dist: numpy<2.0.0,>=1.20.0; extra == "embeddings"
48
48
  Provides-Extra: processing
49
+ Provides-Extra: media
50
+ Requires-Dist: Pillow<12.0.0,>=10.0.0; extra == "media"
51
+ Requires-Dist: pymupdf4llm<1.0.0,>=0.0.20; extra == "media"
52
+ Requires-Dist: unstructured[office]<1.0.0,>=0.10.0; extra == "media"
53
+ Requires-Dist: pandas<3.0.0,>=1.0.0; extra == "media"
49
54
  Provides-Extra: api-providers
50
55
  Requires-Dist: abstractcore[anthropic,openai]; extra == "api-providers"
51
56
  Provides-Extra: local-providers
@@ -55,9 +60,9 @@ Requires-Dist: abstractcore[huggingface]; extra == "heavy-providers"
55
60
  Provides-Extra: all-providers
56
61
  Requires-Dist: abstractcore[anthropic,embeddings,huggingface,lmstudio,mlx,ollama,openai]; extra == "all-providers"
57
62
  Provides-Extra: all
58
- Requires-Dist: abstractcore[anthropic,dev,docs,embeddings,huggingface,lmstudio,mlx,ollama,openai,processing,server,test]; extra == "all"
63
+ Requires-Dist: abstractcore[anthropic,dev,docs,embeddings,huggingface,lmstudio,media,mlx,ollama,openai,processing,server,test]; extra == "all"
59
64
  Provides-Extra: lightweight
60
- Requires-Dist: abstractcore[anthropic,embeddings,lmstudio,ollama,openai,processing,server]; extra == "lightweight"
65
+ Requires-Dist: abstractcore[anthropic,embeddings,lmstudio,media,ollama,openai,processing,server]; extra == "lightweight"
61
66
  Provides-Extra: dev
62
67
  Requires-Dist: pytest>=7.0.0; extra == "dev"
63
68
  Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
@@ -89,6 +94,11 @@ Dynamic: license-file
89
94
 
90
95
  # AbstractCore
91
96
 
97
+ [![PyPI version](https://img.shields.io/pypi/v/abstractcore.svg)](https://pypi.org/project/abstractcore/)
98
+ [![Python Version](https://img.shields.io/pypi/pyversions/abstractcore)](https://pypi.org/project/abstractcore/)
99
+ [![license](https://img.shields.io/github/license/lpalbou/abstractcore)](https://github.com/lpalbou/abstractcore/blob/main/LICENSE)
100
+ [![GitHub stars](https://img.shields.io/github/stars/lpalbou/abstractcore?style=social)](https://github.com/lpalbou/abstractcore/stargazers)
101
+
92
102
  A unified Python library for interaction with multiple Large Language Model (LLM) providers.
93
103
 
94
104
  **Write once, run everywhere.**
@@ -153,15 +163,68 @@ loaded_session = BasicSession.load('conversation.json', provider=llm)
153
163
 
154
164
  [Learn more about Session](docs/session.md)
155
165
 
166
+ ### Media Handling
167
+
168
+ AbstractCore provides **unified media handling** across all providers with **automatic maximum resolution optimization** for best results. Upload images, PDFs, and documents using the same simple API regardless of your provider.
169
+
170
+ ```python
171
+ from abstractcore import create_llm
172
+
173
+ # Vision analysis - works with any vision model
174
+ # Images automatically processed at maximum supported resolution
175
+ llm = create_llm("openai", model="gpt-4o")
176
+ response = llm.generate(
177
+ "What's in this image?",
178
+ media=["photo.jpg"] # Auto-resized to model's maximum capability
179
+ )
180
+
181
+ # Document analysis - works with any model
182
+ llm = create_llm("anthropic", model="claude-3.5-sonnet")
183
+ response = llm.generate(
184
+ "Summarize this research paper",
185
+ media=["research_paper.pdf"]
186
+ )
187
+
188
+ # Multiple files - mix images, PDFs, spreadsheets
189
+ response = llm.generate(
190
+ "Analyze these business documents",
191
+ media=["report.pdf", "chart.png", "data.xlsx"]
192
+ )
193
+
194
+ # Same code works with local models
195
+ llm = create_llm("ollama", model="qwen3-vl:8b")
196
+ response = llm.generate(
197
+ "Describe this screenshot",
198
+ media=["screenshot.png"] # Auto-optimized for qwen3-vl
199
+ )
200
+ ```
201
+
202
+ **Key Features:**
203
+ - **Smart Resolution**: Automatically uses maximum resolution supported by each model
204
+ - **Format Support**: PNG, JPEG, GIF, WEBP, BMP, TIFF images; PDF, TXT, MD, CSV, TSV, JSON documents
205
+ - **Office Documents**: DOCX, XLSX, PPT (with `pip install abstractcore[all]`)
206
+ - **Vision Optimization**: Model-specific image processing for best vision results
207
+
208
+ **Provider compatibility:**
209
+ - **High-resolution vision**: GPT-4o (up to 4096x4096), Claude 3.5 Sonnet (up to 1568x1568)
210
+ - **Local models**: qwen3-vl (up to 3584x3584), gemma3:4b, llama3.2-vision
211
+ - **All models**: Automatic text extraction for non-vision models
212
+
213
+ [Learn more about Media Handling](docs/media-handling-system.md)
214
+
156
215
  ## Key Features
157
216
 
158
217
  - **Provider Agnostic**: Seamlessly switch between OpenAI, Anthropic, Ollama, LMStudio, MLX, HuggingFace
218
+ - **Centralized Configuration**: Global defaults and app-specific preferences at `~/.abstractcore/config/abstractcore.json`
219
+ - **Intelligent Media Handling**: Upload images, PDFs, and documents with automatic maximum resolution optimization
220
+ - **Vision Model Support**: Smart image processing at each model's maximum capability for best results
221
+ - **Document Processing**: Advanced PDF extraction (PyMuPDF4LLM), Office documents (DOCX/XLSX/PPT), CSV/TSV analysis
159
222
  - **Unified Tools**: Consistent tool calling across all providers
160
223
  - **Session Management**: Persistent conversations with metadata, analytics, and complete serialization
161
224
  - **Structured Responses**: Clean, predictable output formats with Pydantic
162
225
  - **Streaming Support**: Real-time token generation for interactive experiences
163
226
  - **Embeddings**: Built-in support for semantic search and RAG applications
164
- - **Universal Server**: Optional OpenAI-compatible API server
227
+ - **Universal Server**: Optional OpenAI-compatible API server with `/v1/responses` endpoint
165
228
 
166
229
  ## Supported Providers
167
230
 
@@ -199,12 +262,15 @@ response = client.chat.completions.create(
199
262
  ```
200
263
 
201
264
  **Server Features:**
202
- - OpenAI-compatible REST endpoints (`/v1/chat/completions`, `/v1/embeddings`, etc.)
265
+ - OpenAI-compatible REST endpoints (`/v1/chat/completions`, `/v1/embeddings`, `/v1/responses`)
266
+ - **NEW in v2.5.0**: OpenAI Responses API (`/v1/responses`) with native `input_file` support
203
267
  - Multi-provider support through one HTTP API
268
+ - Comprehensive media processing (images, PDFs, Office documents, CSV/TSV)
204
269
  - Agentic CLI integration (Codex, Crush, Gemini CLI)
205
- - Streaming responses
270
+ - Streaming responses with optional opt-in
206
271
  - Tool call format conversion
207
- - Interactive API docs at `/docs`
272
+ - Enhanced debug logging with `--debug` flag
273
+ - Interactive API docs at `/docs` (Swagger UI)
208
274
 
209
275
  **When to use the server:**
210
276
  - Integrating with existing OpenAI-compatible tools
@@ -365,6 +431,72 @@ Each application has comprehensive documentation with examples and advanced usag
365
431
  - Integration with shell scripts and automation
366
432
  - Standardized text processing tasks
367
433
 
434
+ ## Configuration
435
+
436
+ AbstractCore provides a **centralized configuration system** that manages default models, cache directories, and logging settings from a single location. This eliminates the need to specify `--provider` and `--model` parameters repeatedly.
437
+
438
+ ### Quick Setup
439
+
440
+ ```bash
441
+ # Check current configuration (shows how to change each setting)
442
+ abstractcore --status
443
+
444
+ # Set defaults for all applications
445
+ abstractcore --set-global-default ollama/llama3:8b
446
+
447
+ # Or configure specific applications (examples of customization)
448
+ abstractcore --set-app-default summarizer openai gpt-4o-mini
449
+ abstractcore --set-app-default extractor ollama qwen3:4b-instruct
450
+ abstractcore --set-app-default judge anthropic claude-3-5-haiku
451
+
452
+ # Configure logging (common examples)
453
+ abstractcore --set-console-log-level WARNING # Reduce console output
454
+ abstractcore --set-console-log-level NONE # Disable console logging
455
+ abstractcore --enable-file-logging # Save logs to files
456
+ abstractcore --enable-debug-logging # Full debug mode
457
+
458
+ # Set API keys as needed
459
+ abstractcore --set-api-key openai sk-your-key-here
460
+ abstractcore --set-api-key anthropic your-anthropic-key
461
+
462
+ # Verify configuration (includes change commands for each setting)
463
+ abstractcore --status
464
+ ```
465
+
466
+ ### Priority System
467
+
468
+ AbstractCore uses a clear priority system where explicit parameters always override defaults:
469
+
470
+ 1. **Explicit parameters** (highest priority): `summarizer doc.txt --provider openai --model gpt-4o-mini`
471
+ 2. **App-specific config**: `abstractcore --set-app-default summarizer openai gpt-4o-mini`
472
+ 3. **Global config**: `abstractcore --set-global-default openai/gpt-4o-mini`
473
+ 4. **Built-in defaults** (lowest priority): `huggingface/unsloth/Qwen3-4B-Instruct-2507-GGUF`
474
+
475
+ ### Usage After Configuration
476
+
477
+ Once configured, apps use your defaults automatically:
478
+
479
+ ```bash
480
+ # Before configuration (requires explicit parameters)
481
+ summarizer document.pdf --provider openai --model gpt-4o-mini
482
+
483
+ # After configuration (uses configured defaults)
484
+ summarizer document.pdf
485
+
486
+ # Explicit parameters still override when needed
487
+ summarizer document.pdf --provider anthropic --model claude-3-5-sonnet
488
+ ```
489
+
490
+ ### Configuration Features
491
+
492
+ - **Application defaults**: Different optimal models for each app
493
+ - **Cache directories**: Configurable cache locations for models and data
494
+ - **Logging control**: Package-wide logging levels and debug mode
495
+ - **API key management**: Centralized API key storage
496
+ - **Interactive setup**: `abstractcore --configure` for guided configuration
497
+
498
+ **Complete guide**: [Centralized Configuration](docs/centralized-config.md)
499
+
368
500
  ## Documentation
369
501
 
370
502
  **📚 Complete Documentation:** [docs/](docs/) - Full documentation index and navigation guide
@@ -376,6 +508,7 @@ Each application has comprehensive documentation with examples and advanced usag
376
508
 
377
509
  ### Core Library (Python)
378
510
  - **[Python API Reference](docs/api-reference.md)** - Complete Python API documentation
511
+ - **[Media Handling System](docs/media-handling-system.md)** - Images, PDFs, and document processing across all providers
379
512
  - **[Session Management](docs/session.md)** - Persistent conversations, serialization, and analytics
380
513
  - **[Embeddings Guide](docs/embeddings.md)** - Semantic search, RAG, and vector embeddings
381
514
  - **[Code Examples](examples/)** - Working examples for all features
@@ -401,7 +534,44 @@ for provider in providers:
401
534
  response = llm.generate("Hello!")
402
535
  ```
403
536
 
404
- ### 2. Local Development, Cloud Production
537
+ ### 2. Vision Analysis Across Providers
538
+
539
+ ```python
540
+ # Same image analysis works with any vision model
541
+ image_files = ["product_photo.jpg", "user_feedback.png"]
542
+ prompt = "Analyze these product images and suggest improvements"
543
+
544
+ # OpenAI GPT-4o
545
+ openai_llm = create_llm("openai", model="gpt-4o")
546
+ openai_analysis = openai_llm.generate(prompt, media=image_files)
547
+
548
+ # Anthropic Claude
549
+ claude_llm = create_llm("anthropic", model="claude-3.5-sonnet")
550
+ claude_analysis = claude_llm.generate(prompt, media=image_files)
551
+
552
+ # Local model (free)
553
+ local_llm = create_llm("ollama", model="qwen3-vl:8b")
554
+ local_analysis = local_llm.generate(prompt, media=image_files)
555
+ ```
556
+
557
+ ### 3. Document Processing Pipeline
558
+
559
+ ```python
560
+ # Universal document analysis
561
+ documents = ["contract.pdf", "financial_data.xlsx", "presentation.ppt"]
562
+ analysis_prompt = "Extract key information and identify potential risks"
563
+
564
+ # Works with any provider
565
+ llm = create_llm("anthropic", model="claude-3.5-sonnet")
566
+ response = llm.generate(analysis_prompt, media=documents)
567
+
568
+ # Automatic format handling:
569
+ # - PDF: Advanced text extraction with PyMuPDF4LLM
570
+ # - Excel: Table parsing with pandas
571
+ # - PowerPoint: Slide content extraction with unstructured
572
+ ```
573
+
574
+ ### 4. Local Development, Cloud Production
405
575
 
406
576
  ```python
407
577
  # Development (free, local)
@@ -411,7 +581,7 @@ llm_dev = create_llm("ollama", model="qwen3:4b-instruct-2507-q4_K_M")
411
581
  llm_prod = create_llm("openai", model="gpt-4o-mini")
412
582
  ```
413
583
 
414
- ### 3. Embeddings & RAG
584
+ ### 5. Embeddings & RAG
415
585
 
416
586
  ```python
417
587
  from abstractcore.embeddings import EmbeddingManager
@@ -431,7 +601,7 @@ similarity = embedder.compute_similarity(query, docs[0])
431
601
 
432
602
  [Learn more about Embeddings](docs/embeddings.md)
433
603
 
434
- ### 4. Structured Output
604
+ ### 6. Structured Output
435
605
 
436
606
  ```python
437
607
  from pydantic import BaseModel
@@ -449,7 +619,7 @@ review = llm.generate(
449
619
  print(f"{review.title}: {review.rating}/5")
450
620
  ```
451
621
 
452
- ### 5. Universal API Server
622
+ ### 7. Universal API Server
453
623
 
454
624
  ```bash
455
625
  # Start server once
@@ -466,14 +636,16 @@ curl -X POST http://localhost:8000/v1/chat/completions \
466
636
 
467
637
  ## Why AbstractCore?
468
638
 
469
- - **Unified Interface**: One API for all LLM providers
470
- - **Production Ready**: Robust error handling, retries, timeouts
471
- - **Type Safe**: Full Pydantic integration for structured outputs
472
- - **Local & Cloud**: Run models locally or use cloud APIs
473
- - **Tool Calling**: Consistent function calling across providers
474
- - **Streaming**: Real-time responses for interactive applications
475
- - **Embeddings**: Built-in vector embeddings for RAG
476
- - **Server Mode**: Optional OpenAI-compatible API server
639
+ - **Unified Interface**: One API for all LLM providers
640
+ - **Multimodal Support**: Upload images, PDFs, and documents across all providers
641
+ - **Vision Models**: Seamless integration with GPT-4o, Claude Vision, qwen3-vl, and more
642
+ - **Production Ready**: Robust error handling, retries, timeouts
643
+ - **Type Safe**: Full Pydantic integration for structured outputs
644
+ - **Local & Cloud**: Run models locally or use cloud APIs
645
+ - **Tool Calling**: Consistent function calling across providers
646
+ - **Streaming**: Real-time responses for interactive applications
647
+ - **Embeddings**: Built-in vector embeddings for RAG
648
+ - **Server Mode**: Optional OpenAI-compatible API server
477
649
  - **Well Documented**: Comprehensive guides and examples
478
650
 
479
651
  ## Installation Options
@@ -482,6 +654,9 @@ curl -X POST http://localhost:8000/v1/chat/completions \
482
654
  # Minimal core
483
655
  pip install abstractcore
484
656
 
657
+ # With media handling (images, PDFs, documents)
658
+ pip install abstractcore[media]
659
+
485
660
  # With specific providers
486
661
  pip install abstractcore[openai]
487
662
  pip install abstractcore[anthropic]
@@ -493,10 +668,25 @@ pip install abstractcore[server]
493
668
  # With embeddings
494
669
  pip install abstractcore[embeddings]
495
670
 
496
- # Everything
671
+ # Everything (recommended)
497
672
  pip install abstractcore[all]
498
673
  ```
499
674
 
675
+ **Media processing extras:**
676
+ ```bash
677
+ # For advanced PDF processing
678
+ pip install pymupdf4llm
679
+
680
+ # For Office documents (DOCX, XLSX, PPT)
681
+ pip install unstructured
682
+
683
+ # For image optimization
684
+ pip install pillow
685
+
686
+ # For data processing (CSV, Excel)
687
+ pip install pandas
688
+ ```
689
+
500
690
  ## Testing Status
501
691
 
502
692
  All tests passing as of October 12th, 2025.
@@ -519,6 +709,10 @@ All tests passing as of October 12th, 2025.
519
709
  - **[🐛 Issues](https://github.com/lpalbou/AbstractCore/issues)** - Report bugs
520
710
  - **[💬 Discussions](https://github.com/lpalbou/AbstractCore/discussions)** - Get help
521
711
 
712
+ ## Contact
713
+ **Maintainer:** Laurent-Philippe Albou
714
+ 📧 Email: [contact@abstractcore.ai](mailto:contact@abstractcore.ai)
715
+
522
716
  ## Contributing
523
717
 
524
718
  We welcome contributions! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
@@ -1,65 +1,72 @@
1
1
  abstractcore/__init__.py,sha256=7ucTDQXINslx23n-vaFOeFzRAYi76Enqak2S3rjaZLU,1861
2
2
  abstractcore/apps/__init__.py,sha256=sgNOv3lYyOWNBC-w6GnRN6aILGCbdkQtNcuQdJz5ghE,31
3
3
  abstractcore/apps/__main__.py,sha256=041daYkoIE1VLEO19IZC5nNIDOQZWNgpB7ogJ3SOlsE,1585
4
+ abstractcore/apps/app_config_utils.py,sha256=GygjtkianBktRvirHya1KbXuB2r5OUDJM6ilvybQWvk,875
4
5
  abstractcore/apps/extractor.py,sha256=OfiqB9l_alH9xCGb6zOD__QJkDjdKOlLZngriVgmn7c,23749
5
6
  abstractcore/apps/judge.py,sha256=nOgxvn-BbhNY6xU9AlTeD1yidTh73AiVlSN7hQCVE2M,23169
6
- abstractcore/apps/summarizer.py,sha256=m7x0iCwJucfsaYE4LK5IGqDy19x5XDJuYLIP6-oNIGI,14139
7
+ abstractcore/apps/summarizer.py,sha256=9aD6KH21w-tv_wGp9MaO2uyJuaU71OemW7KpqrG5t6w,14669
7
8
  abstractcore/architectures/__init__.py,sha256=-4JucAM7JkMWShWKkePoclxrUHRKgaG36UTguJihE0U,1046
8
- abstractcore/architectures/detection.py,sha256=PYYzJ73tXI5ssJOpEL6-mHBmTreYXIq5f4nsfqtrpmw,9465
9
+ abstractcore/architectures/detection.py,sha256=Cxap1pL6qOJjyY22Vc4hzbLALTuuBisPT5UaMotousk,10025
9
10
  abstractcore/architectures/enums.py,sha256=9vIv2vDBEKhxwzwH9iaSAyf-iVj3p8y9loMeN_mYTJ8,3821
10
- abstractcore/assets/architecture_formats.json,sha256=VGAPjodeX_7iKGuX9uVy2Ru7Osx-EZJ3yb2DLu-v0r0,16045
11
- abstractcore/assets/model_capabilities.json,sha256=mSfP5C68I1c4GNLgL0sCDjpSZA3kstS5Yn74xTb3Q5M,36722
11
+ abstractcore/assets/architecture_formats.json,sha256=CIf6SaR_IJs1D7Uvd1K3zWngIXJ_yq3DM_IE3wnpCHY,16076
12
+ abstractcore/assets/model_capabilities.json,sha256=iUkDiljyZUZzPlpYCOFgStXyc6e7dvOfReYQ0HFrX9Q,49703
12
13
  abstractcore/assets/session_schema.json,sha256=b6HTAWxRVlVhAzA7FqaKpunK1yO6jilBOsD5sQkqJTo,10580
13
14
  abstractcore/core/__init__.py,sha256=2h-86U4QkCQ4gzZ4iRusSTMlkODiUS6tKjZHiEXz6rM,684
14
15
  abstractcore/core/enums.py,sha256=BhkVnHC-X1_377JDmqd-2mnem9GdBLqixWlYzlP_FJU,695
15
16
  abstractcore/core/factory.py,sha256=UdrNwQAvifvFS3LMjF5KO87m-2n1bJBryTs9pvesYcI,2804
16
- abstractcore/core/interface.py,sha256=DIA46gR7JN33kLyKbtbAkoME7l7rixtKbFGAQgt7ilk,13693
17
+ abstractcore/core/interface.py,sha256=XTvtP1YY_2dSlSdKWkkDK54VtEeUt97zIDR1tXTtn8Q,13876
17
18
  abstractcore/core/retry.py,sha256=wNlUAxfmvdO_uVWb4iqkhTqd7O1oRwXxqvVQaLXQOw0,14538
18
- abstractcore/core/session.py,sha256=Zjy_oo_Ha_Kufrf4ffE9Sxl2gVXcdpBaYl-IssqoBe4,35200
19
+ abstractcore/core/session.py,sha256=iU_fU7f7E6HxwkV97QnSMJSF6rOIuJyT6sQGf547S6s,35347
19
20
  abstractcore/core/types.py,sha256=KT9Gf9ei4t0QnWBH72fFa8vR7UZSKI-CJyQjU9ynE8g,3642
20
21
  abstractcore/embeddings/__init__.py,sha256=hR3xZyqcRm4c2pq1dIa5lxj_-Bk70Zad802JQN4joWo,637
21
- abstractcore/embeddings/manager.py,sha256=QzDtSna4FDCPg1il7GGe_7p1VknuUHjXAFQa98PgU9A,50048
22
+ abstractcore/embeddings/manager.py,sha256=uFVbRPHx_R-kVMVA7N7_7EzeUmCJCeN9Dv0EV8Jko24,52245
22
23
  abstractcore/embeddings/models.py,sha256=bsPAzL6gv57AVii8O15PT0kxfwRkOml3f3njJN4UDi4,4874
23
24
  abstractcore/events/__init__.py,sha256=UtbdTOeL05kvi7YP91yo4OEqs5UAbKylBvOOEkrUL5E,11065
24
25
  abstractcore/exceptions/__init__.py,sha256=h6y3sdZR6uFMh0iq7z85DfJi01zGQvjVOm1W7l86iVQ,3224
25
- abstractcore/media/__init__.py,sha256=BjWR8OIN2uxoOJBAlbM83VkyUtyDtiXM4AerYzhI9fU,4241
26
+ abstractcore/media/__init__.py,sha256=94k22PRXInaXFlxU7p06IRbyjmOkSlCITDm0gb8d9AI,3202
27
+ abstractcore/media/auto_handler.py,sha256=R3EhifSlGSJ_2oftQ7BdD7nQBMGePHOubuXkqHmcHHQ,13478
28
+ abstractcore/media/base.py,sha256=vWdxscqTGTvd3oc4IzzsBTWhUrznWcqM7M_sFyq6-eE,15971
29
+ abstractcore/media/capabilities.py,sha256=qqKvXGkUT-FNnbFS-EYx8KCT9SZOovO2h4N7ucrHgBA,12844
30
+ abstractcore/media/types.py,sha256=jG-g_2_gzl8eOgEalk9x3Ikhni9GoGfoRjkZWaBhV30,10165
31
+ abstractcore/media/vision_fallback.py,sha256=XcEV5T9ekqd4DRBrhJvxgX5j_puxSlofvuUIfQc2vmg,10629
26
32
  abstractcore/processing/__init__.py,sha256=t6hiakQjcZROT4pw9ZFt2q6fF3vf5VpdMKG2EWlsFd8,540
27
33
  abstractcore/processing/basic_extractor.py,sha256=3x-3BdIHgLvqLnLF6K1-P4qVaLIpAnNIIutaJi7lDQM,49832
28
34
  abstractcore/processing/basic_judge.py,sha256=tKWJrg_tY4vCHzWgXxz0ZjgLXBYYfpMcpG7vl03hJcM,32218
29
35
  abstractcore/processing/basic_summarizer.py,sha256=XHNxMQ_8aLStTeUo6_2JaThlct12Htpz7ORmm0iuJsg,25495
30
36
  abstractcore/providers/__init__.py,sha256=t8Kp4flH5GvZEC6dx-iYJSPeSxMODa2spXb8MqtlPy4,1282
31
- abstractcore/providers/anthropic_provider.py,sha256=BM8Vu89c974yicvFwlsJ5C3N0wR9Kkt1pOszViWCwAQ,19694
32
- abstractcore/providers/base.py,sha256=AJa9KFJGLJvrNrhI-EHaVSzaF1ocOOYc98GnnCjLuag,42824
33
- abstractcore/providers/huggingface_provider.py,sha256=aucyfGFHi67NRU7_5vWp2TEXdBBjb710zz0lQjC_rWo,42283
34
- abstractcore/providers/lmstudio_provider.py,sha256=vhZJWDVu9hiC1wCoZoNODY14axXsy_dv52TQ13bsxmw,16004
35
- abstractcore/providers/mlx_provider.py,sha256=eANGeexmJIS4KWn77fRBOJRkXvwgh7irAtu3kDVIVBA,15629
37
+ abstractcore/providers/anthropic_provider.py,sha256=tcOrARLd1kA4vRkH7MCgy99YIGVaegdCd3-Z8UaKP3Q,20705
38
+ abstractcore/providers/base.py,sha256=5YR64kqTYiCvWtIUBul5QfO0XRZr6_Aiho4atpDOh0o,50579
39
+ abstractcore/providers/huggingface_provider.py,sha256=mJGfi1lgsvjV3Lj4q7KCQZhQqw_o23af40i5WLg150o,47789
40
+ abstractcore/providers/lmstudio_provider.py,sha256=oPL_Y4gkJMAniecdWQVaDi7WozCZumSRs0lE7uFgvQk,20406
41
+ abstractcore/providers/mlx_provider.py,sha256=61i5VhpNw_QlhOwPcEcryaGbI45aYyL9q15TrpinIgs,17427
36
42
  abstractcore/providers/mock_provider.py,sha256=tIjA57Hlwu3vNODOZShNn0tY9HWvz1p4z-HyD_bsvbo,5741
37
- abstractcore/providers/ollama_provider.py,sha256=SkXD5gjuzeu9Thqnt4pRPSi-cjWxwuZGV2x5YMm26jo,19340
38
- abstractcore/providers/openai_provider.py,sha256=xGFrSkbCrsBnWnho1U2aMCBdzfCqf121wU1EFGmU3YQ,21678
43
+ abstractcore/providers/ollama_provider.py,sha256=O77Nzx0erQw8D5TlyVaunIOjluaRwi8bgYVO95qK0L4,21129
44
+ abstractcore/providers/openai_provider.py,sha256=gHurjXwwKvKQtkK5cqwokW_DUTY9_bsfNm06RPvQ39g,22683
39
45
  abstractcore/providers/registry.py,sha256=c0hxp9RRa-uipGotaAu48fHXc_HGlLcOxC1k763mzhU,16596
40
46
  abstractcore/providers/streaming.py,sha256=VnffBV_CU9SAKzghL154OoFyEdDsiLwUNXPahyU41Bw,31342
41
47
  abstractcore/server/__init__.py,sha256=1DSAz_YhQtnKv7sNi5TMQV8GFujctDOabgvAdilQE0o,249
42
- abstractcore/server/app.py,sha256=DCzKEe2hzvIfdq3N410boU0kRocFipRoiFDnABEGkYs,43350
48
+ abstractcore/server/app.py,sha256=2ChRxdpMyGVCjsQPWWpPNtqdtfIIhbpF3uLuTCiF6lo,96629
43
49
  abstractcore/structured/__init__.py,sha256=VXRQHGcm-iaYnLOBPin2kyhvhhQA0kaGt_pcNDGsE_8,339
44
50
  abstractcore/structured/handler.py,sha256=Vb15smiR81JGDXX2RLkY2Exuj67J7a6C-xwVrZoXp0I,17134
45
51
  abstractcore/structured/retry.py,sha256=BN_PvrWybyU1clMy2cult1-TVxFSMaVqiCPmmXvA5aI,3805
46
52
  abstractcore/tools/__init__.py,sha256=oh6vG0RdM1lqUtOp95mLrTsWLh9VmhJf5_FVjGIP5_M,2259
47
- abstractcore/tools/common_tools.py,sha256=kMk7h1X407miLsjCD6fjtB_yuoWHdVycF_ego3ggJqg,64115
53
+ abstractcore/tools/common_tools.py,sha256=jRVvu-TQbmXBOZHn00zEZb44nenaXVtdemhKmRmt1YY,64496
48
54
  abstractcore/tools/core.py,sha256=lUUGihyceiRYlKUFvEMig9jWFF563d574mSDbYYD3fM,4777
49
55
  abstractcore/tools/handler.py,sha256=GmDenXAJkhceWSGlhvuF90aMb2301tRTh6WxGwBQifc,12022
50
56
  abstractcore/tools/parser.py,sha256=1r5nmEEp1Rid3JU6ct-s3lP-eCln67fvXG5HCjqiRew,27740
51
57
  abstractcore/tools/registry.py,sha256=cN3nbPEK6L2vAd9740MIFf2fitW_4WHpQfK4KvQjnT0,9059
52
58
  abstractcore/tools/syntax_rewriter.py,sha256=c3NSTvUF3S3ho5Cwjp7GJJdibeYAI6k3iaBwhKcpTfo,17318
53
59
  abstractcore/tools/tag_rewriter.py,sha256=UGFMBj2QKwm12j8JQ6oc2C_N3ZeNqz9Enz4VkEIrS0c,20338
54
- abstractcore/utils/__init__.py,sha256=lgIRvSf22gMNAJYr2LF3O7IuRL0Qp7UAqZxhFlEcfMc,539
55
- abstractcore/utils/cli.py,sha256=8ua5Lu4bKSs9y1JB5W3kJ0OA-_dFpUHk6prH1U684xc,56888
60
+ abstractcore/utils/__init__.py,sha256=29mMc3jV_suEPBn7W8Yw_wqcdvFP-083ws5AUFJVM28,676
61
+ abstractcore/utils/cli.py,sha256=btuTIECrBPV4cdoWoE-N1rkzcrq--dhwKbtZTQhvwok,65175
62
+ abstractcore/utils/message_preprocessor.py,sha256=GdHkm6tmrgjm3PwHRSCjIsq1XLkbhy_vDEKEUE7OiKY,6028
56
63
  abstractcore/utils/self_fixes.py,sha256=QEDwNTW80iQM4ftfEY3Ghz69F018oKwLM9yeRCYZOvw,5886
57
- abstractcore/utils/structured_logging.py,sha256=Y7TVkf1tP9BCOPNbBY1rQubBxcAxhhUOYMbrV2k50ZM,15830
64
+ abstractcore/utils/structured_logging.py,sha256=Vm-HviSa42G9DJCWmaEv4a0QG3NMsADD3ictLOs4En0,19952
58
65
  abstractcore/utils/token_utils.py,sha256=eLwFmJ68p9WMFD_MHLMmeJRW6Oqx_4hKELB8FNQ2Mnk,21097
59
- abstractcore/utils/version.py,sha256=5z25ZawOKXC7_TsMUhliL7I_XUADAb5ums58hgtaXEc,605
60
- abstractcore-2.4.2.dist-info/licenses/LICENSE,sha256=PI2v_4HMvd6050uDD_4AY_8PzBnu2asa3RKbdDjowTA,1078
61
- abstractcore-2.4.2.dist-info/METADATA,sha256=AMCwg1STsdh8FmhmHw05GNK6ANM8nN1VDvusq7lFEFM,19684
62
- abstractcore-2.4.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
63
- abstractcore-2.4.2.dist-info/entry_points.txt,sha256=Ocy403YwzaOBT7D_vf7w6YFiIQ4nTbp0htjXfeI5IOo,315
64
- abstractcore-2.4.2.dist-info/top_level.txt,sha256=DiNHBI35SIawW3N9Z-z0y6cQYNbXd32pvBkW0RLfScs,13
65
- abstractcore-2.4.2.dist-info/RECORD,,
66
+ abstractcore/utils/version.py,sha256=ykpU7S-ZbOgwDZo_6zYttC6y8VPe3CuV0anATrFRej8,605
67
+ abstractcore-2.4.4.dist-info/licenses/LICENSE,sha256=PI2v_4HMvd6050uDD_4AY_8PzBnu2asa3RKbdDjowTA,1078
68
+ abstractcore-2.4.4.dist-info/METADATA,sha256=h6Wn0s43Xlng3K0bOw4o1LcT293rVoRPgionXu3kQKg,27596
69
+ abstractcore-2.4.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
70
+ abstractcore-2.4.4.dist-info/entry_points.txt,sha256=di-rxy4iD6A3j8DmQHeVmXYlncgCJKZGamyjrQXEfcU,357
71
+ abstractcore-2.4.4.dist-info/top_level.txt,sha256=DiNHBI35SIawW3N9Z-z0y6cQYNbXd32pvBkW0RLfScs,13
72
+ abstractcore-2.4.4.dist-info/RECORD,,
@@ -1,4 +1,5 @@
1
1
  [console_scripts]
2
+ abstractcore = abstractcore.cli.main:main
2
3
  abstractcore-extractor = abstractcore.apps.extractor:main
3
4
  abstractcore-judge = abstractcore.apps.judge:main
4
5
  abstractcore-summarizer = abstractcore.apps.summarizer:main