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.
- {abstractcore-2.4.0 → abstractcore-2.4.2}/PKG-INFO +1 -1
- abstractcore-2.4.2/abstractcore/core/factory.py +73 -0
- abstractcore-2.4.2/abstractcore/exceptions/__init__.py +125 -0
- abstractcore-2.4.2/abstractcore/media/__init__.py +151 -0
- abstractcore-2.4.2/abstractcore/providers/__init__.py +48 -0
- abstractcore-2.4.2/abstractcore/providers/registry.py +406 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/server/app.py +47 -75
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/utils/version.py +1 -1
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore.egg-info/PKG-INFO +1 -1
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore.egg-info/SOURCES.txt +3 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/pyproject.toml +1 -1
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_agentic_cli_compatibility.py +9 -15
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_basic_summarizer.py +3 -13
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_critical_streaming_tool_fix.py +80 -71
- abstractcore-2.4.0/abstractcore/core/factory.py +0 -122
- abstractcore-2.4.0/abstractcore/providers/__init__.py +0 -21
- {abstractcore-2.4.0 → abstractcore-2.4.2}/LICENSE +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/README.md +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/__init__.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/apps/__init__.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/apps/__main__.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/apps/extractor.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/apps/judge.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/apps/summarizer.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/architectures/__init__.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/architectures/detection.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/architectures/enums.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/assets/architecture_formats.json +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/assets/model_capabilities.json +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/assets/session_schema.json +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/core/__init__.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/core/enums.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/core/interface.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/core/retry.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/core/session.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/core/types.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/embeddings/__init__.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/embeddings/manager.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/embeddings/models.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/events/__init__.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/processing/__init__.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/processing/basic_extractor.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/processing/basic_judge.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/processing/basic_summarizer.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/providers/anthropic_provider.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/providers/base.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/providers/huggingface_provider.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/providers/lmstudio_provider.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/providers/mlx_provider.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/providers/mock_provider.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/providers/ollama_provider.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/providers/openai_provider.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/providers/streaming.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/server/__init__.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/structured/__init__.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/structured/handler.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/structured/retry.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/tools/__init__.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/tools/common_tools.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/tools/core.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/tools/handler.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/tools/parser.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/tools/registry.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/tools/syntax_rewriter.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/tools/tag_rewriter.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/utils/__init__.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/utils/cli.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/utils/self_fixes.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/utils/structured_logging.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore/utils/token_utils.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore.egg-info/dependency_links.txt +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore.egg-info/entry_points.txt +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore.egg-info/requires.txt +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/abstractcore.egg-info/top_level.txt +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/setup.cfg +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_all_specified_providers.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_basic_session.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_complete_integration.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_comprehensive_events.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_core_components.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_embeddings.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_embeddings_integration.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_embeddings_llm_integration.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_embeddings_matrix_operations.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_embeddings_no_mock.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_embeddings_real.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_embeddings_semantic_validation.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_embeddings_simple.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_environment_variable_tool_call_tags.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_factory.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_final_comprehensive.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_final_graceful_errors.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_graceful_fallback.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_integrated_functionality.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_ollama_tool_role_fix.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_openai_conversion_manual.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_openai_format_bug.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_openai_format_conversion.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_progressive_complexity.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_provider_basic_session.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_provider_connectivity.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_provider_simple_generation.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_provider_streaming.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_provider_token_translation.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_provider_tool_detection.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_providers.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_providers_comprehensive.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_providers_simple.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_real_models_comprehensive.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_retry_observability.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_retry_strategy.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_server_embeddings_real.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_stream_tool_calling.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_streaming_enhancements.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_streaming_tag_rewriting.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_structured_integration.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_structured_output.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_syntax_rewriter.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_tool_calling.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_tool_execution_separation.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_unified_streaming.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_unload_memory.py +0 -0
- {abstractcore-2.4.0 → abstractcore-2.4.2}/tests/test_user_scenario_validation.py +0 -0
- {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.
|
|
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
|
+
]
|