abstractcore 2.4.1__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.1 → abstractcore-2.4.2}/PKG-INFO +1 -1
- abstractcore-2.4.2/abstractcore/core/factory.py +73 -0
- abstractcore-2.4.2/abstractcore/providers/__init__.py +48 -0
- abstractcore-2.4.2/abstractcore/providers/registry.py +406 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/server/app.py +47 -75
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/utils/version.py +1 -1
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore.egg-info/PKG-INFO +1 -1
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore.egg-info/SOURCES.txt +1 -0
- abstractcore-2.4.1/abstractcore/core/factory.py +0 -122
- abstractcore-2.4.1/abstractcore/providers/__init__.py +0 -21
- {abstractcore-2.4.1 → abstractcore-2.4.2}/LICENSE +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/README.md +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/__init__.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/apps/__init__.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/apps/__main__.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/apps/extractor.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/apps/judge.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/apps/summarizer.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/architectures/__init__.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/architectures/detection.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/architectures/enums.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/assets/architecture_formats.json +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/assets/model_capabilities.json +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/assets/session_schema.json +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/core/__init__.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/core/enums.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/core/interface.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/core/retry.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/core/session.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/core/types.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/embeddings/__init__.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/embeddings/manager.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/embeddings/models.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/events/__init__.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/exceptions/__init__.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/media/__init__.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/processing/__init__.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/processing/basic_extractor.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/processing/basic_judge.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/processing/basic_summarizer.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/providers/anthropic_provider.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/providers/base.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/providers/huggingface_provider.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/providers/lmstudio_provider.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/providers/mlx_provider.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/providers/mock_provider.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/providers/ollama_provider.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/providers/openai_provider.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/providers/streaming.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/server/__init__.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/structured/__init__.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/structured/handler.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/structured/retry.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/tools/__init__.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/tools/common_tools.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/tools/core.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/tools/handler.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/tools/parser.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/tools/registry.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/tools/syntax_rewriter.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/tools/tag_rewriter.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/utils/__init__.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/utils/cli.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/utils/self_fixes.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/utils/structured_logging.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore/utils/token_utils.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore.egg-info/dependency_links.txt +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore.egg-info/entry_points.txt +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore.egg-info/requires.txt +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/abstractcore.egg-info/top_level.txt +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/pyproject.toml +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/setup.cfg +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_agentic_cli_compatibility.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_all_specified_providers.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_basic_session.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_basic_summarizer.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_complete_integration.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_comprehensive_events.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_core_components.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_critical_streaming_tool_fix.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_embeddings.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_embeddings_integration.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_embeddings_llm_integration.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_embeddings_matrix_operations.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_embeddings_no_mock.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_embeddings_real.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_embeddings_semantic_validation.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_embeddings_simple.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_environment_variable_tool_call_tags.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_factory.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_final_comprehensive.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_final_graceful_errors.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_graceful_fallback.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_integrated_functionality.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_ollama_tool_role_fix.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_openai_conversion_manual.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_openai_format_bug.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_openai_format_conversion.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_progressive_complexity.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_provider_basic_session.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_provider_connectivity.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_provider_simple_generation.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_provider_streaming.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_provider_token_translation.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_provider_tool_detection.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_providers.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_providers_comprehensive.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_providers_simple.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_real_models_comprehensive.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_retry_observability.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_retry_strategy.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_server_embeddings_real.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_stream_tool_calling.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_streaming_enhancements.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_streaming_tag_rewriting.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_structured_integration.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_structured_output.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_syntax_rewriter.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_tool_calling.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_tool_execution_separation.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_unified_streaming.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_unload_memory.py +0 -0
- {abstractcore-2.4.1 → abstractcore-2.4.2}/tests/test_user_scenario_validation.py +0 -0
- {abstractcore-2.4.1 → 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,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
|
+
]
|
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Provider Registry - Centralized provider discovery and metadata management.
|
|
3
|
+
|
|
4
|
+
This module provides a single source of truth for all AbstractCore providers,
|
|
5
|
+
eliminating the need for manual synchronization across factory.py, server/app.py,
|
|
6
|
+
and __init__.py files.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from typing import List, Dict, Any, Optional, Type, Callable
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
from abc import ABC
|
|
12
|
+
import logging
|
|
13
|
+
from ..utils.structured_logging import get_logger
|
|
14
|
+
|
|
15
|
+
logger = get_logger("provider_registry")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class ProviderInfo:
|
|
20
|
+
"""Information about a registered provider."""
|
|
21
|
+
name: str
|
|
22
|
+
display_name: str
|
|
23
|
+
provider_class: Type
|
|
24
|
+
description: str
|
|
25
|
+
provider_type: str = "llm"
|
|
26
|
+
default_model: Optional[str] = None
|
|
27
|
+
supported_features: List[str] = field(default_factory=list)
|
|
28
|
+
authentication_required: bool = True
|
|
29
|
+
local_provider: bool = False
|
|
30
|
+
installation_extras: Optional[str] = None
|
|
31
|
+
import_path: str = ""
|
|
32
|
+
|
|
33
|
+
def __post_init__(self):
|
|
34
|
+
"""Set default values after initialization."""
|
|
35
|
+
if not self.import_path:
|
|
36
|
+
self.import_path = f"..providers.{self.name}_provider"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class ProviderRegistry:
|
|
40
|
+
"""
|
|
41
|
+
Centralized registry for all AbstractCore providers.
|
|
42
|
+
|
|
43
|
+
This registry serves as the single source of truth for provider discovery,
|
|
44
|
+
metadata, and instantiation across the entire AbstractCore system.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
def __init__(self):
|
|
48
|
+
self._providers: Dict[str, ProviderInfo] = {}
|
|
49
|
+
self._logger = get_logger("ProviderRegistry")
|
|
50
|
+
self._register_all_providers()
|
|
51
|
+
|
|
52
|
+
def _register_all_providers(self):
|
|
53
|
+
"""Register all available providers with their metadata."""
|
|
54
|
+
|
|
55
|
+
# OpenAI Provider
|
|
56
|
+
self.register_provider(ProviderInfo(
|
|
57
|
+
name="openai",
|
|
58
|
+
display_name="OpenAI",
|
|
59
|
+
provider_class=None, # Will be set during lazy loading
|
|
60
|
+
description="Commercial API with GPT-4, GPT-3.5, and embedding models",
|
|
61
|
+
default_model="gpt-5-nano-2025-08-07",
|
|
62
|
+
supported_features=["chat", "completion", "embeddings", "native_tools", "streaming", "structured_output"],
|
|
63
|
+
authentication_required=True,
|
|
64
|
+
local_provider=False,
|
|
65
|
+
installation_extras="openai",
|
|
66
|
+
import_path="..providers.openai_provider"
|
|
67
|
+
))
|
|
68
|
+
|
|
69
|
+
# Anthropic Provider
|
|
70
|
+
self.register_provider(ProviderInfo(
|
|
71
|
+
name="anthropic",
|
|
72
|
+
display_name="Anthropic",
|
|
73
|
+
provider_class=None,
|
|
74
|
+
description="Commercial API with Claude 3 family models",
|
|
75
|
+
default_model="claude-3-5-haiku-latest",
|
|
76
|
+
supported_features=["chat", "completion", "native_tools", "streaming", "structured_output"],
|
|
77
|
+
authentication_required=True,
|
|
78
|
+
local_provider=False,
|
|
79
|
+
installation_extras="anthropic",
|
|
80
|
+
import_path="..providers.anthropic_provider"
|
|
81
|
+
))
|
|
82
|
+
|
|
83
|
+
# Ollama Provider
|
|
84
|
+
self.register_provider(ProviderInfo(
|
|
85
|
+
name="ollama",
|
|
86
|
+
display_name="Ollama",
|
|
87
|
+
provider_class=None,
|
|
88
|
+
description="Local LLM server for running open-source models",
|
|
89
|
+
default_model="qwen3-coder:30b",
|
|
90
|
+
supported_features=["chat", "completion", "embeddings", "prompted_tools", "streaming"],
|
|
91
|
+
authentication_required=False,
|
|
92
|
+
local_provider=True,
|
|
93
|
+
installation_extras="ollama",
|
|
94
|
+
import_path="..providers.ollama_provider"
|
|
95
|
+
))
|
|
96
|
+
|
|
97
|
+
# LMStudio Provider
|
|
98
|
+
self.register_provider(ProviderInfo(
|
|
99
|
+
name="lmstudio",
|
|
100
|
+
display_name="LMStudio",
|
|
101
|
+
provider_class=None,
|
|
102
|
+
description="Local model development and testing platform",
|
|
103
|
+
default_model="qwen/qwen3-4b-2507",
|
|
104
|
+
supported_features=["chat", "completion", "embeddings", "prompted_tools", "streaming"],
|
|
105
|
+
authentication_required=False,
|
|
106
|
+
local_provider=True,
|
|
107
|
+
installation_extras=None,
|
|
108
|
+
import_path="..providers.lmstudio_provider"
|
|
109
|
+
))
|
|
110
|
+
|
|
111
|
+
# MLX Provider
|
|
112
|
+
self.register_provider(ProviderInfo(
|
|
113
|
+
name="mlx",
|
|
114
|
+
display_name="MLX",
|
|
115
|
+
provider_class=None,
|
|
116
|
+
description="Apple Silicon optimized local inference",
|
|
117
|
+
default_model="mlx-community/Qwen3-4B",
|
|
118
|
+
supported_features=["chat", "completion", "prompted_tools", "streaming", "apple_silicon"],
|
|
119
|
+
authentication_required=False,
|
|
120
|
+
local_provider=True,
|
|
121
|
+
installation_extras="mlx",
|
|
122
|
+
import_path="..providers.mlx_provider"
|
|
123
|
+
))
|
|
124
|
+
|
|
125
|
+
# HuggingFace Provider
|
|
126
|
+
self.register_provider(ProviderInfo(
|
|
127
|
+
name="huggingface",
|
|
128
|
+
display_name="HuggingFace",
|
|
129
|
+
provider_class=None,
|
|
130
|
+
description="Access to HuggingFace models (transformers and embeddings)",
|
|
131
|
+
default_model="Qwen/Qwen3-4B/",
|
|
132
|
+
supported_features=["chat", "completion", "embeddings", "prompted_tools", "local_models"],
|
|
133
|
+
authentication_required=False, # Optional for public models
|
|
134
|
+
local_provider=True,
|
|
135
|
+
installation_extras="huggingface",
|
|
136
|
+
import_path="..providers.huggingface_provider"
|
|
137
|
+
))
|
|
138
|
+
|
|
139
|
+
# Mock Provider
|
|
140
|
+
self.register_provider(ProviderInfo(
|
|
141
|
+
name="mock",
|
|
142
|
+
display_name="Mock",
|
|
143
|
+
provider_class=None,
|
|
144
|
+
description="Testing provider for development and unit tests",
|
|
145
|
+
default_model="mock-model",
|
|
146
|
+
supported_features=["chat", "completion", "embeddings", "prompted_tools", "streaming", "testing"],
|
|
147
|
+
authentication_required=False,
|
|
148
|
+
local_provider=True,
|
|
149
|
+
installation_extras=None,
|
|
150
|
+
import_path="..providers.mock_provider"
|
|
151
|
+
))
|
|
152
|
+
|
|
153
|
+
def register_provider(self, provider_info: ProviderInfo):
|
|
154
|
+
"""Register a provider in the registry."""
|
|
155
|
+
self._providers[provider_info.name] = provider_info
|
|
156
|
+
self._logger.debug(f"Registered provider: {provider_info.name}")
|
|
157
|
+
|
|
158
|
+
def get_provider_info(self, provider_name: str) -> Optional[ProviderInfo]:
|
|
159
|
+
"""Get information about a specific provider."""
|
|
160
|
+
return self._providers.get(provider_name.lower())
|
|
161
|
+
|
|
162
|
+
def list_provider_names(self) -> List[str]:
|
|
163
|
+
"""Get list of all registered provider names."""
|
|
164
|
+
return list(self._providers.keys())
|
|
165
|
+
|
|
166
|
+
def is_provider_available(self, provider_name: str) -> bool:
|
|
167
|
+
"""Check if a provider is registered."""
|
|
168
|
+
return provider_name.lower() in self._providers
|
|
169
|
+
|
|
170
|
+
def get_provider_class(self, provider_name: str):
|
|
171
|
+
"""Get the provider class, loading it lazily if needed."""
|
|
172
|
+
provider_info = self.get_provider_info(provider_name)
|
|
173
|
+
if not provider_info:
|
|
174
|
+
raise ValueError(f"Unknown provider: {provider_name}")
|
|
175
|
+
|
|
176
|
+
# Lazy loading of provider class
|
|
177
|
+
if provider_info.provider_class is None:
|
|
178
|
+
provider_info.provider_class = self._load_provider_class(provider_info)
|
|
179
|
+
|
|
180
|
+
return provider_info.provider_class
|
|
181
|
+
|
|
182
|
+
def _load_provider_class(self, provider_info: ProviderInfo):
|
|
183
|
+
"""Dynamically load a provider class."""
|
|
184
|
+
try:
|
|
185
|
+
if provider_info.name == "mock":
|
|
186
|
+
from ..providers.mock_provider import MockProvider
|
|
187
|
+
return MockProvider
|
|
188
|
+
elif provider_info.name == "openai":
|
|
189
|
+
from ..providers.openai_provider import OpenAIProvider
|
|
190
|
+
return OpenAIProvider
|
|
191
|
+
elif provider_info.name == "anthropic":
|
|
192
|
+
from ..providers.anthropic_provider import AnthropicProvider
|
|
193
|
+
return AnthropicProvider
|
|
194
|
+
elif provider_info.name == "ollama":
|
|
195
|
+
from ..providers.ollama_provider import OllamaProvider
|
|
196
|
+
return OllamaProvider
|
|
197
|
+
elif provider_info.name == "lmstudio":
|
|
198
|
+
from ..providers.lmstudio_provider import LMStudioProvider
|
|
199
|
+
return LMStudioProvider
|
|
200
|
+
elif provider_info.name == "mlx":
|
|
201
|
+
from ..providers.mlx_provider import MLXProvider
|
|
202
|
+
return MLXProvider
|
|
203
|
+
elif provider_info.name == "huggingface":
|
|
204
|
+
from ..providers.huggingface_provider import HuggingFaceProvider
|
|
205
|
+
return HuggingFaceProvider
|
|
206
|
+
else:
|
|
207
|
+
raise ImportError(f"No import logic for provider: {provider_info.name}")
|
|
208
|
+
except ImportError as e:
|
|
209
|
+
self._logger.warning(f"Failed to load provider {provider_info.name}: {e}")
|
|
210
|
+
raise ImportError(
|
|
211
|
+
f"{provider_info.display_name} dependencies not installed. "
|
|
212
|
+
f"Install with: pip install abstractcore[{provider_info.installation_extras}]"
|
|
213
|
+
) from e
|
|
214
|
+
|
|
215
|
+
def get_available_models(self, provider_name: str, **kwargs) -> List[str]:
|
|
216
|
+
"""
|
|
217
|
+
Get available models for a specific provider.
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
provider_name: Name of the provider
|
|
221
|
+
**kwargs: Provider-specific parameters (e.g., api_key, base_url)
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
List of available model names
|
|
225
|
+
"""
|
|
226
|
+
try:
|
|
227
|
+
provider_class = self.get_provider_class(provider_name)
|
|
228
|
+
|
|
229
|
+
# Handle providers that need instance for model listing
|
|
230
|
+
if provider_name in ["anthropic", "ollama", "lmstudio"]:
|
|
231
|
+
provider_info = self.get_provider_info(provider_name)
|
|
232
|
+
# Create minimal instance for API access
|
|
233
|
+
instance = provider_class(model=provider_info.default_model, **kwargs)
|
|
234
|
+
return instance.list_available_models(**kwargs)
|
|
235
|
+
else:
|
|
236
|
+
# Handle providers with static method or class method
|
|
237
|
+
try:
|
|
238
|
+
# First try as static/class method
|
|
239
|
+
return provider_class.list_available_models(**kwargs)
|
|
240
|
+
except TypeError:
|
|
241
|
+
# If that fails (method needs 'self'), create temporary instance
|
|
242
|
+
provider_info = self.get_provider_info(provider_name)
|
|
243
|
+
instance = provider_class(model=provider_info.default_model, **kwargs)
|
|
244
|
+
return instance.list_available_models(**kwargs)
|
|
245
|
+
|
|
246
|
+
except Exception as e:
|
|
247
|
+
self._logger.debug(f"Failed to get models from provider {provider_name}: {e}")
|
|
248
|
+
return []
|
|
249
|
+
|
|
250
|
+
def get_provider_status(self, provider_name: str) -> Dict[str, Any]:
|
|
251
|
+
"""
|
|
252
|
+
Get detailed status information for a provider.
|
|
253
|
+
|
|
254
|
+
Returns provider information including availability, model count, etc.
|
|
255
|
+
This is used by the server /providers endpoint.
|
|
256
|
+
"""
|
|
257
|
+
provider_info = self.get_provider_info(provider_name)
|
|
258
|
+
if not provider_info:
|
|
259
|
+
return {
|
|
260
|
+
"name": provider_name,
|
|
261
|
+
"status": "unknown",
|
|
262
|
+
"error": "Provider not registered"
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
try:
|
|
266
|
+
# Try to get models to test availability
|
|
267
|
+
models = self.get_available_models(provider_name)
|
|
268
|
+
|
|
269
|
+
return {
|
|
270
|
+
"name": provider_info.name,
|
|
271
|
+
"display_name": provider_info.display_name,
|
|
272
|
+
"type": provider_info.provider_type,
|
|
273
|
+
"model_count": len(models),
|
|
274
|
+
"status": "available" if models else "no_models",
|
|
275
|
+
"description": provider_info.description,
|
|
276
|
+
"local_provider": provider_info.local_provider,
|
|
277
|
+
"authentication_required": provider_info.authentication_required,
|
|
278
|
+
"supported_features": provider_info.supported_features,
|
|
279
|
+
"installation_extras": provider_info.installation_extras,
|
|
280
|
+
"models": models
|
|
281
|
+
}
|
|
282
|
+
except Exception as e:
|
|
283
|
+
return {
|
|
284
|
+
"name": provider_info.name,
|
|
285
|
+
"display_name": provider_info.display_name,
|
|
286
|
+
"type": provider_info.provider_type,
|
|
287
|
+
"model_count": 0,
|
|
288
|
+
"status": "error",
|
|
289
|
+
"description": provider_info.description,
|
|
290
|
+
"error": str(e),
|
|
291
|
+
"local_provider": provider_info.local_provider,
|
|
292
|
+
"authentication_required": provider_info.authentication_required,
|
|
293
|
+
"supported_features": provider_info.supported_features,
|
|
294
|
+
"installation_extras": provider_info.installation_extras
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
def get_all_providers_status(self) -> List[Dict[str, Any]]:
|
|
298
|
+
"""Get status information for all registered providers."""
|
|
299
|
+
return [
|
|
300
|
+
self.get_provider_status(provider_name)
|
|
301
|
+
for provider_name in self.list_provider_names()
|
|
302
|
+
]
|
|
303
|
+
|
|
304
|
+
def get_providers_with_models(self) -> List[Dict[str, Any]]:
|
|
305
|
+
"""Get only providers that have available models."""
|
|
306
|
+
all_providers = self.get_all_providers_status()
|
|
307
|
+
return [
|
|
308
|
+
provider for provider in all_providers
|
|
309
|
+
if provider.get("status") == "available" and provider.get("model_count", 0) > 0
|
|
310
|
+
]
|
|
311
|
+
|
|
312
|
+
def create_provider_instance(self, provider_name: str, model: Optional[str] = None, **kwargs):
|
|
313
|
+
"""
|
|
314
|
+
Create a provider instance using the registry.
|
|
315
|
+
|
|
316
|
+
This is used by the factory to create provider instances.
|
|
317
|
+
"""
|
|
318
|
+
provider_info = self.get_provider_info(provider_name)
|
|
319
|
+
if not provider_info:
|
|
320
|
+
available_providers = ", ".join(self.list_provider_names())
|
|
321
|
+
raise ValueError(f"Unknown provider: {provider_name}. Available providers: {available_providers}")
|
|
322
|
+
|
|
323
|
+
provider_class = self.get_provider_class(provider_name)
|
|
324
|
+
model = model or provider_info.default_model
|
|
325
|
+
|
|
326
|
+
try:
|
|
327
|
+
return provider_class(model=model, **kwargs)
|
|
328
|
+
except ImportError as e:
|
|
329
|
+
# Re-raise import errors with helpful message
|
|
330
|
+
if provider_info.installation_extras:
|
|
331
|
+
raise ImportError(
|
|
332
|
+
f"{provider_info.display_name} dependencies not installed. "
|
|
333
|
+
f"Install with: pip install abstractcore[{provider_info.installation_extras}]"
|
|
334
|
+
) from e
|
|
335
|
+
else:
|
|
336
|
+
raise ImportError(f"{provider_info.display_name} provider not available") from e
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
# Global registry instance
|
|
340
|
+
_registry = None
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def get_provider_registry() -> ProviderRegistry:
|
|
344
|
+
"""Get the global provider registry instance."""
|
|
345
|
+
global _registry
|
|
346
|
+
if _registry is None:
|
|
347
|
+
_registry = ProviderRegistry()
|
|
348
|
+
return _registry
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
# Convenience functions for external use
|
|
352
|
+
def list_available_providers() -> List[str]:
|
|
353
|
+
"""Get list of all available provider names."""
|
|
354
|
+
return get_provider_registry().list_provider_names()
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
def get_provider_info(provider_name: str) -> Optional[ProviderInfo]:
|
|
358
|
+
"""Get information about a specific provider."""
|
|
359
|
+
return get_provider_registry().get_provider_info(provider_name)
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def is_provider_available(provider_name: str) -> bool:
|
|
363
|
+
"""Check if a provider is available."""
|
|
364
|
+
return get_provider_registry().is_provider_available(provider_name)
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
def get_all_providers_with_models() -> List[Dict[str, Any]]:
|
|
368
|
+
"""
|
|
369
|
+
Get comprehensive information about all providers with available models.
|
|
370
|
+
|
|
371
|
+
This is the main function that should be used throughout AbstractCore
|
|
372
|
+
for provider discovery and information. It replaces the manual provider
|
|
373
|
+
lists in factory.py and server/app.py.
|
|
374
|
+
|
|
375
|
+
Returns:
|
|
376
|
+
List of provider dictionaries with comprehensive metadata including:
|
|
377
|
+
- name, display_name, type, description
|
|
378
|
+
- model_count, status, supported_features
|
|
379
|
+
- local_provider, authentication_required
|
|
380
|
+
- installation_extras, sample models
|
|
381
|
+
"""
|
|
382
|
+
return get_provider_registry().get_providers_with_models()
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
def get_all_providers_status() -> List[Dict[str, Any]]:
|
|
386
|
+
"""
|
|
387
|
+
Get status information for all registered providers.
|
|
388
|
+
|
|
389
|
+
This includes providers that may not have models available,
|
|
390
|
+
useful for debugging and comprehensive provider listing.
|
|
391
|
+
"""
|
|
392
|
+
return get_provider_registry().get_all_providers_status()
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
def create_provider(provider_name: str, model: Optional[str] = None, **kwargs):
|
|
396
|
+
"""
|
|
397
|
+
Create a provider instance using the centralized registry.
|
|
398
|
+
|
|
399
|
+
This replaces the factory logic and provides better error messages.
|
|
400
|
+
"""
|
|
401
|
+
return get_provider_registry().create_provider_instance(provider_name, model, **kwargs)
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
def get_available_models_for_provider(provider_name: str, **kwargs) -> List[str]:
|
|
405
|
+
"""Get available models for a specific provider."""
|
|
406
|
+
return get_provider_registry().get_available_models(provider_name, **kwargs)
|