abstractcore 2.5.0__py3-none-any.whl → 2.5.3__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.
- abstractcore/__init__.py +12 -0
- abstractcore/apps/__main__.py +8 -1
- abstractcore/apps/deepsearch.py +644 -0
- abstractcore/apps/intent.py +614 -0
- abstractcore/architectures/detection.py +250 -4
- abstractcore/assets/architecture_formats.json +14 -1
- abstractcore/assets/model_capabilities.json +583 -44
- abstractcore/compression/__init__.py +29 -0
- abstractcore/compression/analytics.py +420 -0
- abstractcore/compression/cache.py +250 -0
- abstractcore/compression/config.py +279 -0
- abstractcore/compression/exceptions.py +30 -0
- abstractcore/compression/glyph_processor.py +381 -0
- abstractcore/compression/optimizer.py +388 -0
- abstractcore/compression/orchestrator.py +380 -0
- abstractcore/compression/pil_text_renderer.py +818 -0
- abstractcore/compression/quality.py +226 -0
- abstractcore/compression/text_formatter.py +666 -0
- abstractcore/compression/vision_compressor.py +371 -0
- abstractcore/config/main.py +66 -1
- abstractcore/config/manager.py +111 -5
- abstractcore/core/session.py +105 -5
- abstractcore/events/__init__.py +1 -1
- abstractcore/media/auto_handler.py +312 -18
- abstractcore/media/handlers/local_handler.py +14 -2
- abstractcore/media/handlers/openai_handler.py +62 -3
- abstractcore/media/processors/__init__.py +11 -1
- abstractcore/media/processors/direct_pdf_processor.py +210 -0
- abstractcore/media/processors/glyph_pdf_processor.py +227 -0
- abstractcore/media/processors/image_processor.py +7 -1
- abstractcore/media/processors/text_processor.py +18 -3
- abstractcore/media/types.py +164 -7
- abstractcore/processing/__init__.py +5 -1
- abstractcore/processing/basic_deepsearch.py +2173 -0
- abstractcore/processing/basic_intent.py +690 -0
- abstractcore/providers/__init__.py +18 -0
- abstractcore/providers/anthropic_provider.py +29 -2
- abstractcore/providers/base.py +279 -6
- abstractcore/providers/huggingface_provider.py +658 -27
- abstractcore/providers/lmstudio_provider.py +52 -2
- abstractcore/providers/mlx_provider.py +103 -4
- abstractcore/providers/model_capabilities.py +352 -0
- abstractcore/providers/ollama_provider.py +44 -6
- abstractcore/providers/openai_provider.py +29 -2
- abstractcore/providers/registry.py +91 -19
- abstractcore/server/app.py +91 -81
- abstractcore/structured/handler.py +161 -1
- abstractcore/tools/common_tools.py +98 -3
- abstractcore/utils/__init__.py +4 -1
- abstractcore/utils/cli.py +114 -1
- abstractcore/utils/trace_export.py +287 -0
- abstractcore/utils/version.py +1 -1
- abstractcore/utils/vlm_token_calculator.py +655 -0
- {abstractcore-2.5.0.dist-info → abstractcore-2.5.3.dist-info}/METADATA +140 -23
- abstractcore-2.5.3.dist-info/RECORD +107 -0
- {abstractcore-2.5.0.dist-info → abstractcore-2.5.3.dist-info}/entry_points.txt +4 -0
- abstractcore-2.5.0.dist-info/RECORD +0 -86
- {abstractcore-2.5.0.dist-info → abstractcore-2.5.3.dist-info}/WHEEL +0 -0
- {abstractcore-2.5.0.dist-info → abstractcore-2.5.3.dist-info}/licenses/LICENSE +0 -0
- {abstractcore-2.5.0.dist-info → abstractcore-2.5.3.dist-info}/top_level.txt +0 -0
|
@@ -23,8 +23,10 @@ from ..events import EventType
|
|
|
23
23
|
class OllamaProvider(BaseProvider):
|
|
24
24
|
"""Ollama provider for local models with full integration"""
|
|
25
25
|
|
|
26
|
-
def __init__(self, model: str = "
|
|
26
|
+
def __init__(self, model: str = "qwen3:4b-instruct-2507-q4_K_M", base_url: str = "http://localhost:11434", **kwargs):
|
|
27
27
|
super().__init__(model, **kwargs)
|
|
28
|
+
self.provider = "ollama"
|
|
29
|
+
|
|
28
30
|
self.base_url = base_url.rstrip('/')
|
|
29
31
|
self.client = httpx.Client(timeout=self._timeout)
|
|
30
32
|
|
|
@@ -112,6 +114,7 @@ class OllamaProvider(BaseProvider):
|
|
|
112
114
|
media: Optional[List['MediaContent']] = None,
|
|
113
115
|
stream: bool = False,
|
|
114
116
|
response_model: Optional[Type[BaseModel]] = None,
|
|
117
|
+
media_metadata: Optional[List[Dict[str, Any]]] = None,
|
|
115
118
|
**kwargs) -> Union[GenerateResponse, Iterator[GenerateResponse]]:
|
|
116
119
|
"""Internal generation with Ollama"""
|
|
117
120
|
|
|
@@ -143,9 +146,11 @@ class OllamaProvider(BaseProvider):
|
|
|
143
146
|
payload["options"]["seed"] = seed_value
|
|
144
147
|
|
|
145
148
|
# Add structured output support (Ollama native JSON schema)
|
|
149
|
+
# Ollama accepts the full JSON schema in the "format" parameter
|
|
150
|
+
# This provides server-side guaranteed schema compliance
|
|
146
151
|
if response_model and PYDANTIC_AVAILABLE:
|
|
147
152
|
json_schema = response_model.model_json_schema()
|
|
148
|
-
payload["format"] = json_schema
|
|
153
|
+
payload["format"] = json_schema # Pass the full schema, not just "json"
|
|
149
154
|
|
|
150
155
|
# Use chat format by default (recommended by Ollama docs), especially when tools are present
|
|
151
156
|
# Only use generate format for very simple cases without tools or messages
|
|
@@ -220,9 +225,9 @@ class OllamaProvider(BaseProvider):
|
|
|
220
225
|
if stream:
|
|
221
226
|
return self._stream_generate(endpoint, payload, tools, kwargs.get('tool_call_tags'))
|
|
222
227
|
else:
|
|
223
|
-
return self._single_generate(endpoint, payload, tools)
|
|
228
|
+
return self._single_generate(endpoint, payload, tools, media_metadata)
|
|
224
229
|
|
|
225
|
-
def _single_generate(self, endpoint: str, payload: Dict[str, Any], tools: Optional[List[Dict[str, Any]]] = None) -> GenerateResponse:
|
|
230
|
+
def _single_generate(self, endpoint: str, payload: Dict[str, Any], tools: Optional[List[Dict[str, Any]]] = None, media_metadata: Optional[List[Dict[str, Any]]] = None) -> GenerateResponse:
|
|
226
231
|
"""Generate single response"""
|
|
227
232
|
try:
|
|
228
233
|
# Track generation time
|
|
@@ -258,6 +263,12 @@ class OllamaProvider(BaseProvider):
|
|
|
258
263
|
},
|
|
259
264
|
gen_time=gen_time
|
|
260
265
|
)
|
|
266
|
+
|
|
267
|
+
# Attach media metadata if available
|
|
268
|
+
if media_metadata:
|
|
269
|
+
if not generate_response.metadata:
|
|
270
|
+
generate_response.metadata = {}
|
|
271
|
+
generate_response.metadata['media_metadata'] = media_metadata
|
|
261
272
|
|
|
262
273
|
# Execute tools if enabled and tools are present
|
|
263
274
|
if self.execute_tools and tools and self.tool_handler.supports_prompted and content:
|
|
@@ -442,8 +453,21 @@ class OllamaProvider(BaseProvider):
|
|
|
442
453
|
self.client = httpx.Client(timeout=self._timeout)
|
|
443
454
|
|
|
444
455
|
def list_available_models(self, **kwargs) -> List[str]:
|
|
445
|
-
"""
|
|
456
|
+
"""
|
|
457
|
+
List available models from Ollama server.
|
|
458
|
+
|
|
459
|
+
Args:
|
|
460
|
+
**kwargs: Optional parameters including:
|
|
461
|
+
- base_url: Ollama server URL
|
|
462
|
+
- input_capabilities: List of ModelInputCapability enums to filter by input capability
|
|
463
|
+
- output_capabilities: List of ModelOutputCapability enums to filter by output capability
|
|
464
|
+
|
|
465
|
+
Returns:
|
|
466
|
+
List of model names, optionally filtered by capabilities
|
|
467
|
+
"""
|
|
446
468
|
try:
|
|
469
|
+
from .model_capabilities import filter_models_by_capabilities
|
|
470
|
+
|
|
447
471
|
# Use provided base_url or fall back to instance base_url
|
|
448
472
|
base_url = kwargs.get('base_url', self.base_url)
|
|
449
473
|
|
|
@@ -451,7 +475,21 @@ class OllamaProvider(BaseProvider):
|
|
|
451
475
|
if response.status_code == 200:
|
|
452
476
|
data = response.json()
|
|
453
477
|
models = [model["name"] for model in data.get("models", [])]
|
|
454
|
-
|
|
478
|
+
models = sorted(models)
|
|
479
|
+
|
|
480
|
+
# Apply new capability filtering if provided
|
|
481
|
+
input_capabilities = kwargs.get('input_capabilities')
|
|
482
|
+
output_capabilities = kwargs.get('output_capabilities')
|
|
483
|
+
|
|
484
|
+
if input_capabilities or output_capabilities:
|
|
485
|
+
models = filter_models_by_capabilities(
|
|
486
|
+
models,
|
|
487
|
+
input_capabilities=input_capabilities,
|
|
488
|
+
output_capabilities=output_capabilities
|
|
489
|
+
)
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
return models
|
|
455
493
|
else:
|
|
456
494
|
self.logger.warning(f"Ollama API returned status {response.status_code}")
|
|
457
495
|
return []
|
|
@@ -32,6 +32,7 @@ class OpenAIProvider(BaseProvider):
|
|
|
32
32
|
|
|
33
33
|
def __init__(self, model: str = "gpt-3.5-turbo", api_key: Optional[str] = None, **kwargs):
|
|
34
34
|
super().__init__(model, **kwargs)
|
|
35
|
+
self.provider = "openai"
|
|
35
36
|
|
|
36
37
|
if not OPENAI_AVAILABLE:
|
|
37
38
|
raise ImportError("OpenAI package not installed. Install with: pip install openai")
|
|
@@ -510,9 +511,21 @@ class OpenAIProvider(BaseProvider):
|
|
|
510
511
|
|
|
511
512
|
@classmethod
|
|
512
513
|
def list_available_models(cls, **kwargs) -> List[str]:
|
|
513
|
-
"""
|
|
514
|
+
"""
|
|
515
|
+
List available models from OpenAI API.
|
|
516
|
+
|
|
517
|
+
Args:
|
|
518
|
+
**kwargs: Optional parameters including:
|
|
519
|
+
- api_key: OpenAI API key
|
|
520
|
+
- input_capabilities: List of ModelInputCapability enums to filter by input capability
|
|
521
|
+
- output_capabilities: List of ModelOutputCapability enums to filter by output capability
|
|
522
|
+
|
|
523
|
+
Returns:
|
|
524
|
+
List of model names, optionally filtered by capabilities
|
|
525
|
+
"""
|
|
514
526
|
try:
|
|
515
527
|
import openai
|
|
528
|
+
from .model_capabilities import filter_models_by_capabilities
|
|
516
529
|
|
|
517
530
|
# Get API key from kwargs or environment
|
|
518
531
|
api_key = kwargs.get('api_key') or os.getenv("OPENAI_API_KEY")
|
|
@@ -541,7 +554,21 @@ class OpenAIProvider(BaseProvider):
|
|
|
541
554
|
]):
|
|
542
555
|
chat_models.append(model_id)
|
|
543
556
|
|
|
544
|
-
|
|
557
|
+
chat_models = sorted(chat_models, reverse=True) # Latest models first
|
|
558
|
+
|
|
559
|
+
# Apply new capability filtering if provided
|
|
560
|
+
input_capabilities = kwargs.get('input_capabilities')
|
|
561
|
+
output_capabilities = kwargs.get('output_capabilities')
|
|
562
|
+
|
|
563
|
+
if input_capabilities or output_capabilities:
|
|
564
|
+
chat_models = filter_models_by_capabilities(
|
|
565
|
+
chat_models,
|
|
566
|
+
input_capabilities=input_capabilities,
|
|
567
|
+
output_capabilities=output_capabilities
|
|
568
|
+
)
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
return chat_models
|
|
545
572
|
|
|
546
573
|
except Exception:
|
|
547
574
|
return []
|
|
@@ -86,8 +86,8 @@ class ProviderRegistry:
|
|
|
86
86
|
display_name="Ollama",
|
|
87
87
|
provider_class=None,
|
|
88
88
|
description="Local LLM server for running open-source models",
|
|
89
|
-
default_model="qwen3-
|
|
90
|
-
supported_features=["chat", "completion", "embeddings", "prompted_tools", "streaming"],
|
|
89
|
+
default_model="qwen3:4b-instruct-2507-q4_K_M",
|
|
90
|
+
supported_features=["chat", "completion", "embeddings", "prompted_tools", "streaming", "structured_output"],
|
|
91
91
|
authentication_required=False,
|
|
92
92
|
local_provider=True,
|
|
93
93
|
installation_extras="ollama",
|
|
@@ -101,7 +101,7 @@ class ProviderRegistry:
|
|
|
101
101
|
provider_class=None,
|
|
102
102
|
description="Local model development and testing platform",
|
|
103
103
|
default_model="qwen/qwen3-4b-2507",
|
|
104
|
-
supported_features=["chat", "completion", "embeddings", "prompted_tools", "streaming"],
|
|
104
|
+
supported_features=["chat", "completion", "embeddings", "prompted_tools", "streaming", "structured_output"],
|
|
105
105
|
authentication_required=False,
|
|
106
106
|
local_provider=True,
|
|
107
107
|
installation_extras=None,
|
|
@@ -115,7 +115,7 @@ class ProviderRegistry:
|
|
|
115
115
|
provider_class=None,
|
|
116
116
|
description="Apple Silicon optimized local inference",
|
|
117
117
|
default_model="mlx-community/Qwen3-4B",
|
|
118
|
-
supported_features=["chat", "completion", "prompted_tools", "streaming", "apple_silicon"],
|
|
118
|
+
supported_features=["chat", "completion", "prompted_tools", "streaming", "structured_output", "apple_silicon"],
|
|
119
119
|
authentication_required=False,
|
|
120
120
|
local_provider=True,
|
|
121
121
|
installation_extras="mlx",
|
|
@@ -128,8 +128,8 @@ class ProviderRegistry:
|
|
|
128
128
|
display_name="HuggingFace",
|
|
129
129
|
provider_class=None,
|
|
130
130
|
description="Access to HuggingFace models (transformers and embeddings)",
|
|
131
|
-
default_model="
|
|
132
|
-
supported_features=["chat", "completion", "embeddings", "prompted_tools", "local_models"],
|
|
131
|
+
default_model="unsloth/Qwen3-4B-Instruct-2507-GGUF",
|
|
132
|
+
supported_features=["chat", "completion", "embeddings", "prompted_tools", "local_models", "structured_output"],
|
|
133
133
|
authentication_required=False, # Optional for public models
|
|
134
134
|
local_provider=True,
|
|
135
135
|
installation_extras="huggingface",
|
|
@@ -202,10 +202,14 @@ class ProviderRegistry:
|
|
|
202
202
|
|
|
203
203
|
Args:
|
|
204
204
|
provider_name: Name of the provider
|
|
205
|
-
**kwargs: Provider-specific parameters
|
|
205
|
+
**kwargs: Provider-specific parameters including:
|
|
206
|
+
- api_key: API key for authentication (if required)
|
|
207
|
+
- base_url: Base URL for API endpoint (if applicable)
|
|
208
|
+
- input_capabilities: List of ModelInputCapability enums to filter by input capability
|
|
209
|
+
- output_capabilities: List of ModelOutputCapability enums to filter by output capability
|
|
206
210
|
|
|
207
211
|
Returns:
|
|
208
|
-
List of available model names
|
|
212
|
+
List of available model names, optionally filtered by capabilities
|
|
209
213
|
"""
|
|
210
214
|
try:
|
|
211
215
|
provider_class = self.get_provider_class(provider_name)
|
|
@@ -285,13 +289,64 @@ class ProviderRegistry:
|
|
|
285
289
|
for provider_name in self.list_provider_names()
|
|
286
290
|
]
|
|
287
291
|
|
|
288
|
-
def get_providers_with_models(self) -> List[Dict[str, Any]]:
|
|
289
|
-
"""
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
292
|
+
def get_providers_with_models(self, include_models: bool = True) -> List[Dict[str, Any]]:
|
|
293
|
+
"""
|
|
294
|
+
Get only providers that have available models.
|
|
295
|
+
|
|
296
|
+
Args:
|
|
297
|
+
include_models: If True, include actual model lists (slower).
|
|
298
|
+
If False, return metadata only (much faster). Default: True.
|
|
299
|
+
"""
|
|
300
|
+
if include_models:
|
|
301
|
+
# Original behavior - get full status including model lists
|
|
302
|
+
all_providers = self.get_all_providers_status()
|
|
303
|
+
return [
|
|
304
|
+
provider for provider in all_providers
|
|
305
|
+
if provider.get("status") == "available" and provider.get("model_count", 0) > 0
|
|
306
|
+
]
|
|
307
|
+
else:
|
|
308
|
+
# Fast path - get all provider metadata without model enumeration
|
|
309
|
+
# Note: We return all providers since we can't quickly determine which have models
|
|
310
|
+
return self.get_providers_metadata_only()
|
|
311
|
+
|
|
312
|
+
def get_providers_metadata_only(self) -> List[Dict[str, Any]]:
|
|
313
|
+
"""
|
|
314
|
+
Get provider metadata without enumerating models (fast path).
|
|
315
|
+
|
|
316
|
+
This method returns provider information without making API calls
|
|
317
|
+
or scanning for models, making it extremely fast for UI discovery.
|
|
318
|
+
"""
|
|
319
|
+
providers_metadata = []
|
|
320
|
+
|
|
321
|
+
for provider_name in self.list_provider_names():
|
|
322
|
+
provider_info = self.get_provider_info(provider_name)
|
|
323
|
+
if not provider_info:
|
|
324
|
+
continue
|
|
325
|
+
|
|
326
|
+
# Basic availability check without model enumeration
|
|
327
|
+
try:
|
|
328
|
+
provider_class = self.get_provider_class(provider_name)
|
|
329
|
+
status = "available" # Assume available if class can be imported
|
|
330
|
+
except Exception:
|
|
331
|
+
status = "error"
|
|
332
|
+
|
|
333
|
+
metadata = {
|
|
334
|
+
"name": provider_info.name,
|
|
335
|
+
"display_name": provider_info.display_name,
|
|
336
|
+
"type": provider_info.provider_type,
|
|
337
|
+
"model_count": "unknown", # Don't enumerate models
|
|
338
|
+
"status": status,
|
|
339
|
+
"description": provider_info.description,
|
|
340
|
+
"local_provider": provider_info.local_provider,
|
|
341
|
+
"authentication_required": provider_info.authentication_required,
|
|
342
|
+
"supported_features": provider_info.supported_features,
|
|
343
|
+
"installation_extras": provider_info.installation_extras,
|
|
344
|
+
"models": [] # Empty list for fast response
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
providers_metadata.append(metadata)
|
|
348
|
+
|
|
349
|
+
return providers_metadata
|
|
295
350
|
|
|
296
351
|
def create_provider_instance(self, provider_name: str, model: Optional[str] = None, **kwargs):
|
|
297
352
|
"""
|
|
@@ -348,7 +403,7 @@ def is_provider_available(provider_name: str) -> bool:
|
|
|
348
403
|
return get_provider_registry().is_provider_available(provider_name)
|
|
349
404
|
|
|
350
405
|
|
|
351
|
-
def get_all_providers_with_models() -> List[Dict[str, Any]]:
|
|
406
|
+
def get_all_providers_with_models(include_models: bool = True) -> List[Dict[str, Any]]:
|
|
352
407
|
"""
|
|
353
408
|
Get comprehensive information about all providers with available models.
|
|
354
409
|
|
|
@@ -356,14 +411,18 @@ def get_all_providers_with_models() -> List[Dict[str, Any]]:
|
|
|
356
411
|
for provider discovery and information. It replaces the manual provider
|
|
357
412
|
lists in factory.py and server/app.py.
|
|
358
413
|
|
|
414
|
+
Args:
|
|
415
|
+
include_models: If True, include actual model lists (slower).
|
|
416
|
+
If False, return metadata only (much faster). Default: True.
|
|
417
|
+
|
|
359
418
|
Returns:
|
|
360
419
|
List of provider dictionaries with comprehensive metadata including:
|
|
361
420
|
- name, display_name, type, description
|
|
362
421
|
- model_count, status, supported_features
|
|
363
422
|
- local_provider, authentication_required
|
|
364
|
-
- installation_extras, sample models
|
|
423
|
+
- installation_extras, sample models (if include_models=True)
|
|
365
424
|
"""
|
|
366
|
-
return get_provider_registry().get_providers_with_models()
|
|
425
|
+
return get_provider_registry().get_providers_with_models(include_models=include_models)
|
|
367
426
|
|
|
368
427
|
|
|
369
428
|
def get_all_providers_status() -> List[Dict[str, Any]]:
|
|
@@ -386,5 +445,18 @@ def create_provider(provider_name: str, model: Optional[str] = None, **kwargs):
|
|
|
386
445
|
|
|
387
446
|
|
|
388
447
|
def get_available_models_for_provider(provider_name: str, **kwargs) -> List[str]:
|
|
389
|
-
"""
|
|
448
|
+
"""
|
|
449
|
+
Get available models for a specific provider.
|
|
450
|
+
|
|
451
|
+
Args:
|
|
452
|
+
provider_name: Name of the provider
|
|
453
|
+
**kwargs: Provider-specific parameters including:
|
|
454
|
+
- api_key: API key for authentication (if required)
|
|
455
|
+
- base_url: Base URL for API endpoint (if applicable)
|
|
456
|
+
- input_capabilities: List of ModelInputCapability enums to filter by input capability
|
|
457
|
+
- output_capabilities: List of ModelOutputCapability enums to filter by output capability
|
|
458
|
+
|
|
459
|
+
Returns:
|
|
460
|
+
List of available model names, optionally filtered by capabilities
|
|
461
|
+
"""
|
|
390
462
|
return get_provider_registry().get_available_models(provider_name, **kwargs)
|
abstractcore/server/app.py
CHANGED
|
@@ -261,53 +261,43 @@ async def general_exception_handler(request: Request, exc: Exception):
|
|
|
261
261
|
# Model Type Detection
|
|
262
262
|
# ============================================================================
|
|
263
263
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
TEXT_GENERATION = "text-generation"
|
|
267
|
-
TEXT_EMBEDDING = "text-embedding"
|
|
264
|
+
# Import the core capability enums directly
|
|
265
|
+
from ..providers.model_capabilities import ModelInputCapability, ModelOutputCapability
|
|
268
266
|
|
|
269
|
-
def is_embedding_model(model_name: str) -> bool:
|
|
270
|
-
"""
|
|
271
|
-
Detect if a model is an embedding model based on naming heuristics.
|
|
272
|
-
|
|
273
|
-
Args:
|
|
274
|
-
model_name: The model name to check
|
|
275
|
-
|
|
276
|
-
Returns:
|
|
277
|
-
True if the model appears to be an embedding model
|
|
278
|
-
"""
|
|
279
|
-
model_lower = model_name.lower()
|
|
280
|
-
|
|
281
|
-
# Heuristics for embedding models
|
|
282
|
-
embedding_patterns = [
|
|
283
|
-
"embed", # Most embedding models contain "embed"
|
|
284
|
-
"all-minilm", # Sentence-transformers MiniLM models
|
|
285
|
-
"all-mpnet", # Sentence-transformers MPNet models
|
|
286
|
-
"nomic-embed", # Nomic embedding models
|
|
287
|
-
"bert-", # BERT models (e.g., bert-base-uncased)
|
|
288
|
-
"-bert", # BERT-based embedding models (e.g., nomic-bert-2048)
|
|
289
|
-
"bge-", # BAAI BGE embedding models
|
|
290
|
-
"gte-", # GTE embedding models
|
|
291
|
-
"e5-", # E5 embedding models
|
|
292
|
-
"instructor-", # Instructor embedding models
|
|
293
|
-
"granite-embedding", # IBM Granite embedding models
|
|
294
|
-
]
|
|
295
|
-
|
|
296
|
-
return any(pattern in model_lower for pattern in embedding_patterns)
|
|
297
267
|
|
|
298
268
|
# ============================================================================
|
|
299
269
|
# Provider Model Discovery (Using Centralized Registry)
|
|
300
270
|
# ============================================================================
|
|
301
271
|
|
|
302
|
-
def get_models_from_provider(
|
|
303
|
-
|
|
272
|
+
def get_models_from_provider(
|
|
273
|
+
provider_name: str,
|
|
274
|
+
input_capabilities=None,
|
|
275
|
+
output_capabilities=None
|
|
276
|
+
) -> List[str]:
|
|
277
|
+
"""
|
|
278
|
+
Get available models from a specific provider using the centralized provider registry.
|
|
279
|
+
|
|
280
|
+
Args:
|
|
281
|
+
provider_name: Name of the provider
|
|
282
|
+
input_capabilities: Optional list of ModelInputCapability enums
|
|
283
|
+
output_capabilities: Optional list of ModelOutputCapability enums
|
|
284
|
+
|
|
285
|
+
Returns:
|
|
286
|
+
List of model names from the provider, optionally filtered
|
|
287
|
+
"""
|
|
304
288
|
try:
|
|
305
289
|
from ..providers.registry import get_available_models_for_provider
|
|
306
|
-
return get_available_models_for_provider(
|
|
290
|
+
return get_available_models_for_provider(
|
|
291
|
+
provider_name,
|
|
292
|
+
input_capabilities=input_capabilities,
|
|
293
|
+
output_capabilities=output_capabilities
|
|
294
|
+
)
|
|
307
295
|
except Exception as e:
|
|
308
296
|
logger.debug(f"Failed to get models from provider {provider_name}: {e}")
|
|
309
297
|
return []
|
|
310
298
|
|
|
299
|
+
|
|
300
|
+
|
|
311
301
|
# ============================================================================
|
|
312
302
|
# OpenAI Responses API Models (100% Compatible)
|
|
313
303
|
# ============================================================================
|
|
@@ -994,43 +984,47 @@ async def list_models(
|
|
|
994
984
|
description="Filter by provider (e.g., 'ollama', 'openai', 'anthropic', 'lmstudio')",
|
|
995
985
|
example=""
|
|
996
986
|
),
|
|
997
|
-
|
|
987
|
+
input_type: Optional[ModelInputCapability] = Query(
|
|
998
988
|
None,
|
|
999
|
-
description="Filter by
|
|
1000
|
-
|
|
1001
|
-
|
|
989
|
+
description="Filter by input capability: 'text', 'image', 'audio', 'video'"
|
|
990
|
+
),
|
|
991
|
+
output_type: Optional[ModelOutputCapability] = Query(
|
|
992
|
+
None,
|
|
993
|
+
description="Filter by output capability: 'text', 'embeddings'"
|
|
994
|
+
),
|
|
1002
995
|
):
|
|
1003
996
|
"""
|
|
1004
997
|
List available models from AbstractCore providers.
|
|
1005
|
-
|
|
1006
|
-
Returns a list of all available models, optionally filtered by provider and/or
|
|
1007
|
-
|
|
1008
|
-
**
|
|
1009
|
-
- `
|
|
1010
|
-
- `
|
|
1011
|
-
|
|
998
|
+
|
|
999
|
+
Returns a list of all available models, optionally filtered by provider and/or capabilities.
|
|
1000
|
+
|
|
1001
|
+
**Filtering System:**
|
|
1002
|
+
- `input_type`: Filter by what INPUT the model can process (text, image, audio, video)
|
|
1003
|
+
- `output_type`: Filter by what OUTPUT the model generates (text, embeddings)
|
|
1004
|
+
|
|
1012
1005
|
**Examples:**
|
|
1013
1006
|
- `/v1/models` - All models from all providers
|
|
1014
|
-
- `/v1/models?
|
|
1015
|
-
- `/v1/models?
|
|
1016
|
-
- `/v1/models?
|
|
1017
|
-
- `/v1/models?provider=ollama&
|
|
1007
|
+
- `/v1/models?output_type=embeddings` - Only embedding models
|
|
1008
|
+
- `/v1/models?input_type=text&output_type=text` - Text-only models that generate text
|
|
1009
|
+
- `/v1/models?input_type=image` - Models that can analyze images
|
|
1010
|
+
- `/v1/models?provider=ollama&input_type=image` - Ollama vision models only
|
|
1018
1011
|
"""
|
|
1019
1012
|
try:
|
|
1020
1013
|
models_data = []
|
|
1021
1014
|
|
|
1015
|
+
# Use the capability enums directly
|
|
1016
|
+
input_capabilities = [input_type] if input_type else None
|
|
1017
|
+
output_capabilities = [output_type] if output_type else None
|
|
1018
|
+
|
|
1019
|
+
|
|
1022
1020
|
if provider:
|
|
1023
|
-
# Get models from specific provider
|
|
1024
|
-
models = get_models_from_provider(
|
|
1021
|
+
# Get models from specific provider with optional filtering
|
|
1022
|
+
models = get_models_from_provider(
|
|
1023
|
+
provider.lower(),
|
|
1024
|
+
input_capabilities=input_capabilities,
|
|
1025
|
+
output_capabilities=output_capabilities
|
|
1026
|
+
)
|
|
1025
1027
|
for model in models:
|
|
1026
|
-
# Apply type filter if specified
|
|
1027
|
-
if type:
|
|
1028
|
-
is_embedding = is_embedding_model(model)
|
|
1029
|
-
if type == ModelType.TEXT_EMBEDDING and not is_embedding:
|
|
1030
|
-
continue # Skip non-embedding models
|
|
1031
|
-
if type == ModelType.TEXT_GENERATION and is_embedding:
|
|
1032
|
-
continue # Skip embedding models
|
|
1033
|
-
|
|
1034
1028
|
model_id = f"{provider.lower()}/{model}"
|
|
1035
1029
|
models_data.append({
|
|
1036
1030
|
"id": model_id,
|
|
@@ -1040,23 +1034,25 @@ async def list_models(
|
|
|
1040
1034
|
"permission": [{"allow_create_engine": False, "allow_sampling": True}]
|
|
1041
1035
|
})
|
|
1042
1036
|
|
|
1043
|
-
|
|
1037
|
+
filter_parts = []
|
|
1038
|
+
if input_type:
|
|
1039
|
+
filter_parts.append(f"input_type={input_type.value}")
|
|
1040
|
+
if output_type:
|
|
1041
|
+
filter_parts.append(f"output_type={output_type.value}")
|
|
1042
|
+
|
|
1043
|
+
filter_msg = f" ({', '.join(filter_parts)})" if filter_parts else ""
|
|
1044
1044
|
logger.info(f"Listed {len(models_data)} models for provider {provider}{filter_msg}")
|
|
1045
1045
|
else:
|
|
1046
1046
|
# Get models from all providers using centralized registry
|
|
1047
1047
|
from ..providers.registry import list_available_providers
|
|
1048
1048
|
providers = list_available_providers()
|
|
1049
1049
|
for prov in providers:
|
|
1050
|
-
models = get_models_from_provider(
|
|
1050
|
+
models = get_models_from_provider(
|
|
1051
|
+
prov,
|
|
1052
|
+
input_capabilities=input_capabilities,
|
|
1053
|
+
output_capabilities=output_capabilities
|
|
1054
|
+
)
|
|
1051
1055
|
for model in models:
|
|
1052
|
-
# Apply type filter if specified
|
|
1053
|
-
if type:
|
|
1054
|
-
is_embedding = is_embedding_model(model)
|
|
1055
|
-
if type == ModelType.TEXT_EMBEDDING and not is_embedding:
|
|
1056
|
-
continue # Skip non-embedding models
|
|
1057
|
-
if type == ModelType.TEXT_GENERATION and is_embedding:
|
|
1058
|
-
continue # Skip embedding models
|
|
1059
|
-
|
|
1060
1056
|
model_id = f"{prov}/{model}"
|
|
1061
1057
|
models_data.append({
|
|
1062
1058
|
"id": model_id,
|
|
@@ -1066,7 +1062,13 @@ async def list_models(
|
|
|
1066
1062
|
"permission": [{"allow_create_engine": False, "allow_sampling": True}]
|
|
1067
1063
|
})
|
|
1068
1064
|
|
|
1069
|
-
|
|
1065
|
+
filter_parts = []
|
|
1066
|
+
if input_type:
|
|
1067
|
+
filter_parts.append(f"input_type={input_type.value}")
|
|
1068
|
+
if output_type:
|
|
1069
|
+
filter_parts.append(f"output_type={output_type.value}")
|
|
1070
|
+
|
|
1071
|
+
filter_msg = f" ({', '.join(filter_parts)})" if filter_parts else ""
|
|
1070
1072
|
logger.info(f"Listed {len(models_data)} models from all providers{filter_msg}")
|
|
1071
1073
|
|
|
1072
1074
|
return {
|
|
@@ -1082,18 +1084,31 @@ async def list_models(
|
|
|
1082
1084
|
}
|
|
1083
1085
|
|
|
1084
1086
|
@app.get("/providers")
|
|
1085
|
-
async def list_providers(
|
|
1087
|
+
async def list_providers(
|
|
1088
|
+
include_models: bool = Query(
|
|
1089
|
+
False,
|
|
1090
|
+
description="Include model lists for each provider. Set to true for full information (slower)."
|
|
1091
|
+
)
|
|
1092
|
+
):
|
|
1086
1093
|
"""
|
|
1087
1094
|
List all available AbstractCore providers and their capabilities.
|
|
1088
1095
|
|
|
1089
1096
|
Returns comprehensive information about all registered LLM providers, including:
|
|
1090
1097
|
- Provider name, display name, and type
|
|
1091
|
-
- Number of available models and sample models
|
|
1098
|
+
- Number of available models and sample models (if include_models=True)
|
|
1092
1099
|
- Current availability status and detailed error information
|
|
1093
1100
|
- Provider description and supported features
|
|
1094
1101
|
- Authentication requirements and installation instructions
|
|
1095
1102
|
- Local vs. cloud provider designation
|
|
1096
1103
|
|
|
1104
|
+
**Query Parameters:**
|
|
1105
|
+
- `include_models` (bool, default=False): Include model lists for each provider.
|
|
1106
|
+
Set to `true` for full information (slower).
|
|
1107
|
+
|
|
1108
|
+
**Performance:**
|
|
1109
|
+
- `include_models=false`: Metadata only (very fast, ~15ms) - **DEFAULT**
|
|
1110
|
+
- `include_models=true`: Full information including model lists (slower, ~800ms)
|
|
1111
|
+
|
|
1097
1112
|
**Supported Providers:**
|
|
1098
1113
|
- **OpenAI**: Commercial API with GPT-4, GPT-3.5, and embedding models
|
|
1099
1114
|
- **Anthropic**: Commercial API with Claude 3 family models
|
|
@@ -1103,24 +1118,19 @@ async def list_providers():
|
|
|
1103
1118
|
- **HuggingFace**: Access to HuggingFace models (transformers and embeddings)
|
|
1104
1119
|
|
|
1105
1120
|
**Use Cases:**
|
|
1106
|
-
-
|
|
1107
|
-
-
|
|
1121
|
+
- Fast provider discovery: `GET /providers` (default, very fast)
|
|
1122
|
+
- Full provider information: `GET /providers?include_models=true`
|
|
1108
1123
|
- Build dynamic provider selection UIs
|
|
1109
1124
|
- Monitor provider status and troubleshoot issues
|
|
1110
1125
|
- Get installation instructions for missing dependencies
|
|
1111
1126
|
|
|
1112
|
-
**Enhanced Information:**
|
|
1113
|
-
This endpoint now uses the centralized provider registry to provide
|
|
1114
|
-
comprehensive information including supported features, authentication
|
|
1115
|
-
requirements, and detailed status information.
|
|
1116
|
-
|
|
1117
1127
|
**Returns:** A list of provider objects with comprehensive metadata.
|
|
1118
1128
|
"""
|
|
1119
1129
|
try:
|
|
1120
1130
|
from ..providers.registry import get_all_providers_with_models, get_all_providers_status
|
|
1121
1131
|
|
|
1122
1132
|
# Get providers with models (available providers)
|
|
1123
|
-
available_providers = get_all_providers_with_models()
|
|
1133
|
+
available_providers = get_all_providers_with_models(include_models=include_models)
|
|
1124
1134
|
|
|
1125
1135
|
# Optionally include all providers (even those with issues) for debugging
|
|
1126
1136
|
# Uncomment the next line if you want to see providers with errors too:
|