abstractcore 2.4.0__py3-none-any.whl → 2.4.1__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.
@@ -0,0 +1,125 @@
1
+ """
2
+ Custom exceptions for AbstractCore.
3
+ """
4
+
5
+
6
+ class AbstractCoreError(Exception):
7
+ """Base exception for AbstractCore"""
8
+ pass
9
+
10
+
11
+ class ProviderError(AbstractCoreError):
12
+ """Base exception for provider-related errors"""
13
+ pass
14
+
15
+
16
+ class ProviderAPIError(ProviderError):
17
+ """API call to provider failed"""
18
+ pass
19
+
20
+
21
+ class AuthenticationError(ProviderError):
22
+ """Authentication with provider failed"""
23
+ pass
24
+
25
+
26
+ # Alias for backward compatibility with old AbstractCore
27
+ Authentication = AuthenticationError
28
+
29
+
30
+ class RateLimitError(ProviderError):
31
+ """Rate limit exceeded"""
32
+ pass
33
+
34
+
35
+ class InvalidRequestError(ProviderError):
36
+ """Invalid request to provider"""
37
+ pass
38
+
39
+
40
+ class UnsupportedFeatureError(AbstractCoreError):
41
+ """Feature not supported by provider"""
42
+ pass
43
+
44
+
45
+ class FileProcessingError(AbstractCoreError):
46
+ """Error processing file or media"""
47
+ pass
48
+
49
+
50
+ class ToolExecutionError(AbstractCoreError):
51
+ """Error executing tool"""
52
+ pass
53
+
54
+
55
+ class SessionError(AbstractCoreError):
56
+ """Error with session management"""
57
+ pass
58
+
59
+
60
+ class ConfigurationError(AbstractCoreError):
61
+ """Invalid configuration"""
62
+ pass
63
+
64
+
65
+ class ModelNotFoundError(ProviderError):
66
+ """Model not found or invalid model name"""
67
+ pass
68
+
69
+
70
+ def format_model_error(provider: str, invalid_model: str, available_models: list) -> str:
71
+ """
72
+ Format a helpful error message for model not found errors.
73
+
74
+ Args:
75
+ provider: Provider name (e.g., "OpenAI", "Anthropic")
76
+ invalid_model: The model name that was not found
77
+ available_models: List of available model names
78
+
79
+ Returns:
80
+ Formatted error message string
81
+ """
82
+ message = f"❌ Model '{invalid_model}' not found for {provider} provider.\n"
83
+
84
+ if available_models:
85
+ message += f"\n✅ Available models ({len(available_models)}):\n"
86
+ for model in available_models[:30]: # Show max 30
87
+ message += f" • {model}\n"
88
+ if len(available_models) > 30:
89
+ message += f" ... and {len(available_models) - 30} more\n"
90
+ else:
91
+ # Show provider documentation when we can't fetch models
92
+ doc_links = {
93
+ "anthropic": "https://docs.anthropic.com/en/docs/about-claude/models",
94
+ "openai": "https://platform.openai.com/docs/models",
95
+ "ollama": "https://ollama.com/library",
96
+ "huggingface": "https://huggingface.co/models",
97
+ "mlx": "https://huggingface.co/mlx-community"
98
+ }
99
+
100
+ provider_lower = provider.lower()
101
+ if provider_lower in doc_links:
102
+ message += f"\n📚 See available models: {doc_links[provider_lower]}\n"
103
+ else:
104
+ message += f"\n⚠️ Could not fetch available models for {provider}.\n"
105
+
106
+ return message.rstrip()
107
+
108
+
109
+ # Export all exceptions for easy importing
110
+ __all__ = [
111
+ 'AbstractCoreError',
112
+ 'ProviderError',
113
+ 'ProviderAPIError',
114
+ 'AuthenticationError',
115
+ 'Authentication', # Backward compatibility alias
116
+ 'RateLimitError',
117
+ 'InvalidRequestError',
118
+ 'UnsupportedFeatureError',
119
+ 'FileProcessingError',
120
+ 'ToolExecutionError',
121
+ 'SessionError',
122
+ 'ConfigurationError',
123
+ 'ModelNotFoundError',
124
+ 'format_model_error'
125
+ ]
@@ -0,0 +1,151 @@
1
+ """
2
+ Media handling for different providers.
3
+ """
4
+
5
+ import base64
6
+ from pathlib import Path
7
+ from typing import Union, Dict, Any, Optional
8
+ from enum import Enum
9
+
10
+
11
+ class MediaType(Enum):
12
+ """Supported media types"""
13
+ IMAGE = "image"
14
+ AUDIO = "audio"
15
+ VIDEO = "video"
16
+ DOCUMENT = "document"
17
+
18
+
19
+ class MediaHandler:
20
+ """Base class for media handling"""
21
+
22
+ @staticmethod
23
+ def encode_image(image_path: Union[str, Path]) -> str:
24
+ """
25
+ Encode an image file to base64.
26
+
27
+ Args:
28
+ image_path: Path to the image file
29
+
30
+ Returns:
31
+ Base64 encoded string
32
+ """
33
+ with open(image_path, "rb") as image_file:
34
+ return base64.b64encode(image_file.read()).decode('utf-8')
35
+
36
+ @staticmethod
37
+ def format_for_openai(image_path: Union[str, Path]) -> Dict[str, Any]:
38
+ """
39
+ Format image for OpenAI API.
40
+
41
+ Args:
42
+ image_path: Path to the image
43
+
44
+ Returns:
45
+ Formatted content for OpenAI
46
+ """
47
+ base64_image = MediaHandler.encode_image(image_path)
48
+ return {
49
+ "type": "image_url",
50
+ "image_url": {
51
+ "url": f"data:image/jpeg;base64,{base64_image}"
52
+ }
53
+ }
54
+
55
+ @staticmethod
56
+ def format_for_anthropic(image_path: Union[str, Path]) -> Dict[str, Any]:
57
+ """
58
+ Format image for Anthropic API.
59
+
60
+ Args:
61
+ image_path: Path to the image
62
+
63
+ Returns:
64
+ Formatted content for Anthropic
65
+ """
66
+ base64_image = MediaHandler.encode_image(image_path)
67
+
68
+ # Detect image type
69
+ path = Path(image_path)
70
+ media_type = "image/jpeg"
71
+ if path.suffix.lower() == ".png":
72
+ media_type = "image/png"
73
+ elif path.suffix.lower() == ".gif":
74
+ media_type = "image/gif"
75
+ elif path.suffix.lower() == ".webp":
76
+ media_type = "image/webp"
77
+
78
+ return {
79
+ "type": "image",
80
+ "source": {
81
+ "type": "base64",
82
+ "media_type": media_type,
83
+ "data": base64_image
84
+ }
85
+ }
86
+
87
+ @staticmethod
88
+ def format_for_provider(image_path: Union[str, Path], provider: str) -> Optional[Dict[str, Any]]:
89
+ """
90
+ Format media for a specific provider.
91
+
92
+ Args:
93
+ image_path: Path to the media file
94
+ provider: Provider name
95
+
96
+ Returns:
97
+ Formatted content or None if not supported
98
+ """
99
+ provider_lower = provider.lower()
100
+
101
+ if provider_lower == "openai":
102
+ return MediaHandler.format_for_openai(image_path)
103
+ elif provider_lower == "anthropic":
104
+ return MediaHandler.format_for_anthropic(image_path)
105
+ else:
106
+ # Local providers typically don't support images directly
107
+ return None
108
+
109
+ @staticmethod
110
+ def is_image_file(path: Union[str, Path]) -> bool:
111
+ """
112
+ Check if a file is an image.
113
+
114
+ Args:
115
+ path: Path to check
116
+
117
+ Returns:
118
+ True if the file is an image
119
+ """
120
+ image_extensions = {'.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp', '.ico', '.tiff'}
121
+ return Path(path).suffix.lower() in image_extensions
122
+
123
+ @staticmethod
124
+ def get_media_type(path: Union[str, Path]) -> MediaType:
125
+ """
126
+ Determine the media type of a file.
127
+
128
+ Args:
129
+ path: Path to the file
130
+
131
+ Returns:
132
+ MediaType enum value
133
+ """
134
+ path = Path(path)
135
+ extension = path.suffix.lower()
136
+
137
+ image_extensions = {'.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp'}
138
+ audio_extensions = {'.mp3', '.wav', '.m4a', '.ogg', '.flac'}
139
+ video_extensions = {'.mp4', '.avi', '.mov', '.mkv', '.webm'}
140
+ document_extensions = {'.pdf', '.doc', '.docx', '.txt', '.md'}
141
+
142
+ if extension in image_extensions:
143
+ return MediaType.IMAGE
144
+ elif extension in audio_extensions:
145
+ return MediaType.AUDIO
146
+ elif extension in video_extensions:
147
+ return MediaType.VIDEO
148
+ elif extension in document_extensions:
149
+ return MediaType.DOCUMENT
150
+ else:
151
+ return MediaType.DOCUMENT # Default to document
@@ -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.0"
14
+ __version__ = "2.4.1"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: abstractcore
3
- Version: 2.4.0
3
+ Version: 2.4.1
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>
@@ -21,6 +21,8 @@ abstractcore/embeddings/__init__.py,sha256=hR3xZyqcRm4c2pq1dIa5lxj_-Bk70Zad802JQ
21
21
  abstractcore/embeddings/manager.py,sha256=QzDtSna4FDCPg1il7GGe_7p1VknuUHjXAFQa98PgU9A,50048
22
22
  abstractcore/embeddings/models.py,sha256=bsPAzL6gv57AVii8O15PT0kxfwRkOml3f3njJN4UDi4,4874
23
23
  abstractcore/events/__init__.py,sha256=UtbdTOeL05kvi7YP91yo4OEqs5UAbKylBvOOEkrUL5E,11065
24
+ abstractcore/exceptions/__init__.py,sha256=h6y3sdZR6uFMh0iq7z85DfJi01zGQvjVOm1W7l86iVQ,3224
25
+ abstractcore/media/__init__.py,sha256=BjWR8OIN2uxoOJBAlbM83VkyUtyDtiXM4AerYzhI9fU,4241
24
26
  abstractcore/processing/__init__.py,sha256=t6hiakQjcZROT4pw9ZFt2q6fF3vf5VpdMKG2EWlsFd8,540
25
27
  abstractcore/processing/basic_extractor.py,sha256=3x-3BdIHgLvqLnLF6K1-P4qVaLIpAnNIIutaJi7lDQM,49832
26
28
  abstractcore/processing/basic_judge.py,sha256=tKWJrg_tY4vCHzWgXxz0ZjgLXBYYfpMcpG7vl03hJcM,32218
@@ -53,10 +55,10 @@ abstractcore/utils/cli.py,sha256=8ua5Lu4bKSs9y1JB5W3kJ0OA-_dFpUHk6prH1U684xc,568
53
55
  abstractcore/utils/self_fixes.py,sha256=QEDwNTW80iQM4ftfEY3Ghz69F018oKwLM9yeRCYZOvw,5886
54
56
  abstractcore/utils/structured_logging.py,sha256=Y7TVkf1tP9BCOPNbBY1rQubBxcAxhhUOYMbrV2k50ZM,15830
55
57
  abstractcore/utils/token_utils.py,sha256=eLwFmJ68p9WMFD_MHLMmeJRW6Oqx_4hKELB8FNQ2Mnk,21097
56
- abstractcore/utils/version.py,sha256=IM2ECuDR2OHXGSdAqLdQib35efGRt7ZUzbD9SUnyky4,605
57
- abstractcore-2.4.0.dist-info/licenses/LICENSE,sha256=PI2v_4HMvd6050uDD_4AY_8PzBnu2asa3RKbdDjowTA,1078
58
- abstractcore-2.4.0.dist-info/METADATA,sha256=vqJTIn2uX7-YACF6DbTWCgZhDVCjMRR3VDuKf-HxdV4,19684
59
- abstractcore-2.4.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
60
- abstractcore-2.4.0.dist-info/entry_points.txt,sha256=Ocy403YwzaOBT7D_vf7w6YFiIQ4nTbp0htjXfeI5IOo,315
61
- abstractcore-2.4.0.dist-info/top_level.txt,sha256=DiNHBI35SIawW3N9Z-z0y6cQYNbXd32pvBkW0RLfScs,13
62
- abstractcore-2.4.0.dist-info/RECORD,,
58
+ abstractcore/utils/version.py,sha256=JrNRwMH_7TeJA9dClL5VSUDlkmqpSyPb0K4nPEHGWhM,605
59
+ abstractcore-2.4.1.dist-info/licenses/LICENSE,sha256=PI2v_4HMvd6050uDD_4AY_8PzBnu2asa3RKbdDjowTA,1078
60
+ abstractcore-2.4.1.dist-info/METADATA,sha256=BT2bJNHwvtFL842P1tWRYtjIaO0fIHiMiFn1QwJUviE,19684
61
+ abstractcore-2.4.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
62
+ abstractcore-2.4.1.dist-info/entry_points.txt,sha256=Ocy403YwzaOBT7D_vf7w6YFiIQ4nTbp0htjXfeI5IOo,315
63
+ abstractcore-2.4.1.dist-info/top_level.txt,sha256=DiNHBI35SIawW3N9Z-z0y6cQYNbXd32pvBkW0RLfScs,13
64
+ abstractcore-2.4.1.dist-info/RECORD,,