abstractcore 2.4.0__tar.gz → 2.4.2__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 (124) hide show
  1. {abstractcore-2.4.0 → abstractcore-2.4.2}/PKG-INFO +1 -1
  2. abstractcore-2.4.2/abstractcore/core/factory.py +73 -0
  3. abstractcore-2.4.2/abstractcore/exceptions/__init__.py +125 -0
  4. abstractcore-2.4.2/abstractcore/media/__init__.py +151 -0
  5. abstractcore-2.4.2/abstractcore/providers/__init__.py +48 -0
  6. abstractcore-2.4.2/abstractcore/providers/registry.py +406 -0
  7. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/server/app.py +47 -75
  8. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/utils/version.py +1 -1
  9. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore.egg-info/PKG-INFO +1 -1
  10. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore.egg-info/SOURCES.txt +3 -0
  11. {abstractcore-2.4.0 → abstractcore-2.4.2}/pyproject.toml +1 -1
  12. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_agentic_cli_compatibility.py +9 -15
  13. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_basic_summarizer.py +3 -13
  14. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_critical_streaming_tool_fix.py +80 -71
  15. abstractcore-2.4.0/abstractcore/core/factory.py +0 -122
  16. abstractcore-2.4.0/abstractcore/providers/__init__.py +0 -21
  17. {abstractcore-2.4.0 → abstractcore-2.4.2}/LICENSE +0 -0
  18. {abstractcore-2.4.0 → abstractcore-2.4.2}/README.md +0 -0
  19. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/__init__.py +0 -0
  20. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/apps/__init__.py +0 -0
  21. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/apps/__main__.py +0 -0
  22. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/apps/extractor.py +0 -0
  23. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/apps/judge.py +0 -0
  24. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/apps/summarizer.py +0 -0
  25. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/architectures/__init__.py +0 -0
  26. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/architectures/detection.py +0 -0
  27. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/architectures/enums.py +0 -0
  28. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/assets/architecture_formats.json +0 -0
  29. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/assets/model_capabilities.json +0 -0
  30. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/assets/session_schema.json +0 -0
  31. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/core/__init__.py +0 -0
  32. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/core/enums.py +0 -0
  33. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/core/interface.py +0 -0
  34. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/core/retry.py +0 -0
  35. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/core/session.py +0 -0
  36. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/core/types.py +0 -0
  37. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/embeddings/__init__.py +0 -0
  38. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/embeddings/manager.py +0 -0
  39. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/embeddings/models.py +0 -0
  40. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/events/__init__.py +0 -0
  41. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/processing/__init__.py +0 -0
  42. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/processing/basic_extractor.py +0 -0
  43. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/processing/basic_judge.py +0 -0
  44. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/processing/basic_summarizer.py +0 -0
  45. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/providers/anthropic_provider.py +0 -0
  46. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/providers/base.py +0 -0
  47. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/providers/huggingface_provider.py +0 -0
  48. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/providers/lmstudio_provider.py +0 -0
  49. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/providers/mlx_provider.py +0 -0
  50. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/providers/mock_provider.py +0 -0
  51. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/providers/ollama_provider.py +0 -0
  52. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/providers/openai_provider.py +0 -0
  53. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/providers/streaming.py +0 -0
  54. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/server/__init__.py +0 -0
  55. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/structured/__init__.py +0 -0
  56. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/structured/handler.py +0 -0
  57. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/structured/retry.py +0 -0
  58. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/tools/__init__.py +0 -0
  59. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/tools/common_tools.py +0 -0
  60. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/tools/core.py +0 -0
  61. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/tools/handler.py +0 -0
  62. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/tools/parser.py +0 -0
  63. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/tools/registry.py +0 -0
  64. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/tools/syntax_rewriter.py +0 -0
  65. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/tools/tag_rewriter.py +0 -0
  66. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/utils/__init__.py +0 -0
  67. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/utils/cli.py +0 -0
  68. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/utils/self_fixes.py +0 -0
  69. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/utils/structured_logging.py +0 -0
  70. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/utils/token_utils.py +0 -0
  71. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore.egg-info/dependency_links.txt +0 -0
  72. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore.egg-info/entry_points.txt +0 -0
  73. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore.egg-info/requires.txt +0 -0
  74. {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore.egg-info/top_level.txt +0 -0
  75. {abstractcore-2.4.0 → abstractcore-2.4.2}/setup.cfg +0 -0
  76. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_all_specified_providers.py +0 -0
  77. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_basic_session.py +0 -0
  78. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_complete_integration.py +0 -0
  79. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_comprehensive_events.py +0 -0
  80. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_core_components.py +0 -0
  81. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_embeddings.py +0 -0
  82. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_embeddings_integration.py +0 -0
  83. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_embeddings_llm_integration.py +0 -0
  84. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_embeddings_matrix_operations.py +0 -0
  85. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_embeddings_no_mock.py +0 -0
  86. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_embeddings_real.py +0 -0
  87. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_embeddings_semantic_validation.py +0 -0
  88. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_embeddings_simple.py +0 -0
  89. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_environment_variable_tool_call_tags.py +0 -0
  90. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_factory.py +0 -0
  91. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_final_comprehensive.py +0 -0
  92. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_final_graceful_errors.py +0 -0
  93. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_graceful_fallback.py +0 -0
  94. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_integrated_functionality.py +0 -0
  95. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_ollama_tool_role_fix.py +0 -0
  96. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_openai_conversion_manual.py +0 -0
  97. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_openai_format_bug.py +0 -0
  98. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_openai_format_conversion.py +0 -0
  99. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_progressive_complexity.py +0 -0
  100. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_provider_basic_session.py +0 -0
  101. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_provider_connectivity.py +0 -0
  102. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_provider_simple_generation.py +0 -0
  103. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_provider_streaming.py +0 -0
  104. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_provider_token_translation.py +0 -0
  105. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_provider_tool_detection.py +0 -0
  106. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_providers.py +0 -0
  107. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_providers_comprehensive.py +0 -0
  108. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_providers_simple.py +0 -0
  109. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_real_models_comprehensive.py +0 -0
  110. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_retry_observability.py +0 -0
  111. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_retry_strategy.py +0 -0
  112. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_server_embeddings_real.py +0 -0
  113. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_stream_tool_calling.py +0 -0
  114. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_streaming_enhancements.py +0 -0
  115. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_streaming_tag_rewriting.py +0 -0
  116. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_structured_integration.py +0 -0
  117. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_structured_output.py +0 -0
  118. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_syntax_rewriter.py +0 -0
  119. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_tool_calling.py +0 -0
  120. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_tool_execution_separation.py +0 -0
  121. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_unified_streaming.py +0 -0
  122. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_unload_memory.py +0 -0
  123. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_user_scenario_validation.py +0 -0
  124. {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_wrong_model_fallback.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: abstractcore
3
- Version: 2.4.0
3
+ Version: 2.4.2
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>
@@ -0,0 +1,73 @@
1
+ """
2
+ Factory for creating LLM providers.
3
+ """
4
+
5
+ from typing import Optional
6
+ from .interface import AbstractCoreInterface
7
+ from ..exceptions import ModelNotFoundError, AuthenticationError, ProviderAPIError
8
+
9
+
10
+ def create_llm(provider: str, model: Optional[str] = None, **kwargs) -> AbstractCoreInterface:
11
+ """
12
+ Create an LLM provider instance with unified token parameter support.
13
+
14
+ Args:
15
+ provider: Provider name (openai, anthropic, ollama, huggingface, mlx, lmstudio, mock)
16
+ model: Model name (optional, will use provider default)
17
+ **kwargs: Additional configuration including token parameters
18
+
19
+ Token Parameters (AbstractCore Unified Standard):
20
+ max_tokens: Total context window budget (input + output combined)
21
+ max_output_tokens: Maximum tokens reserved for generation (default: 2048)
22
+ max_input_tokens: Maximum tokens for input (auto-calculated if not specified)
23
+
24
+ Examples:
25
+ # Strategy 1: Budget + Output Reserve (Recommended)
26
+ llm = create_llm(
27
+ provider="openai",
28
+ model="gpt-4o",
29
+ max_tokens=8000, # Total budget
30
+ max_output_tokens=2000 # Reserve for output
31
+ )
32
+
33
+ # Strategy 2: Explicit Input + Output (Advanced)
34
+ llm = create_llm(
35
+ provider="anthropic",
36
+ model="claude-3.5-sonnet",
37
+ max_input_tokens=6000, # Explicit input limit
38
+ max_output_tokens=2000 # Explicit output limit
39
+ )
40
+
41
+ # Quick setup with defaults
42
+ llm = create_llm("ollama", "qwen3-coder:30b")
43
+
44
+ # Get configuration help
45
+ print(llm.get_token_configuration_summary())
46
+ warnings = llm.validate_token_constraints()
47
+
48
+ Returns:
49
+ Configured LLM provider instance with unified token management
50
+
51
+ Raises:
52
+ ImportError: If provider dependencies are not installed
53
+ ValueError: If provider is not supported
54
+ ModelNotFoundError: If specified model is not available
55
+ AuthenticationError: If API credentials are invalid
56
+ """
57
+
58
+ # Auto-detect provider from model name if needed
59
+ if model:
60
+ # MLX models should use MLX provider
61
+ if "mlx-community" in model.lower() and provider.lower() == "huggingface":
62
+ provider = "mlx"
63
+ # GGUF models should use HuggingFace GGUF backend
64
+ elif (".gguf" in model.lower() or "-gguf" in model.lower()) and provider.lower() == "mlx":
65
+ provider = "huggingface"
66
+
67
+ # Use centralized provider registry for all provider creation
68
+ try:
69
+ from ..providers.registry import create_provider
70
+ return create_provider(provider, model, **kwargs)
71
+ except (ModelNotFoundError, AuthenticationError, ProviderAPIError) as e:
72
+ # Re-raise provider exceptions cleanly
73
+ raise e
@@ -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
@@ -0,0 +1,48 @@
1
+ # LLM provider implementations
2
+
3
+ from .base import BaseProvider
4
+ from .openai_provider import OpenAIProvider
5
+ from .anthropic_provider import AnthropicProvider
6
+ from .ollama_provider import OllamaProvider
7
+ from .lmstudio_provider import LMStudioProvider
8
+ from .huggingface_provider import HuggingFaceProvider
9
+ from .mlx_provider import MLXProvider
10
+ from .mock_provider import MockProvider
11
+
12
+ # Provider registry for centralized provider discovery and management
13
+ from .registry import (
14
+ ProviderRegistry,
15
+ ProviderInfo,
16
+ get_provider_registry,
17
+ list_available_providers,
18
+ get_provider_info,
19
+ is_provider_available,
20
+ get_all_providers_with_models,
21
+ get_all_providers_status,
22
+ create_provider,
23
+ get_available_models_for_provider
24
+ )
25
+
26
+ __all__ = [
27
+ # Provider classes
28
+ 'BaseProvider',
29
+ 'OpenAIProvider',
30
+ 'AnthropicProvider',
31
+ 'OllamaProvider',
32
+ 'LMStudioProvider',
33
+ 'HuggingFaceProvider',
34
+ 'MLXProvider',
35
+ 'MockProvider',
36
+
37
+ # Provider registry
38
+ 'ProviderRegistry',
39
+ 'ProviderInfo',
40
+ 'get_provider_registry',
41
+ 'list_available_providers',
42
+ 'get_provider_info',
43
+ 'is_provider_available',
44
+ 'get_all_providers_with_models',
45
+ 'get_all_providers_status',
46
+ 'create_provider',
47
+ 'get_available_models_for_provider',
48
+ ]