abstractcore 2.9.1__py3-none-any.whl → 2.11.4__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 +7 -27
- abstractcore/apps/deepsearch.py +9 -4
- abstractcore/apps/extractor.py +33 -100
- abstractcore/apps/intent.py +19 -0
- abstractcore/apps/judge.py +20 -1
- abstractcore/apps/summarizer.py +20 -1
- abstractcore/architectures/detection.py +34 -1
- abstractcore/architectures/response_postprocessing.py +313 -0
- abstractcore/assets/architecture_formats.json +38 -8
- abstractcore/assets/model_capabilities.json +882 -160
- abstractcore/compression/__init__.py +1 -2
- abstractcore/compression/glyph_processor.py +6 -4
- abstractcore/config/main.py +52 -20
- abstractcore/config/manager.py +390 -12
- abstractcore/config/vision_config.py +5 -5
- abstractcore/core/interface.py +151 -3
- abstractcore/core/session.py +16 -10
- abstractcore/download.py +1 -1
- abstractcore/embeddings/manager.py +20 -6
- abstractcore/endpoint/__init__.py +2 -0
- abstractcore/endpoint/app.py +458 -0
- abstractcore/mcp/client.py +3 -1
- abstractcore/media/__init__.py +52 -17
- abstractcore/media/auto_handler.py +42 -22
- abstractcore/media/base.py +44 -1
- abstractcore/media/capabilities.py +12 -33
- abstractcore/media/enrichment.py +105 -0
- abstractcore/media/handlers/anthropic_handler.py +19 -28
- abstractcore/media/handlers/local_handler.py +124 -70
- abstractcore/media/handlers/openai_handler.py +19 -31
- abstractcore/media/processors/__init__.py +4 -2
- abstractcore/media/processors/audio_processor.py +57 -0
- abstractcore/media/processors/office_processor.py +8 -3
- abstractcore/media/processors/pdf_processor.py +46 -3
- abstractcore/media/processors/text_processor.py +22 -24
- abstractcore/media/processors/video_processor.py +58 -0
- abstractcore/media/types.py +97 -4
- abstractcore/media/utils/image_scaler.py +20 -2
- abstractcore/media/utils/video_frames.py +219 -0
- abstractcore/media/vision_fallback.py +136 -22
- abstractcore/processing/__init__.py +32 -3
- abstractcore/processing/basic_deepsearch.py +15 -10
- abstractcore/processing/basic_intent.py +3 -2
- abstractcore/processing/basic_judge.py +3 -2
- abstractcore/processing/basic_summarizer.py +1 -1
- abstractcore/providers/__init__.py +3 -1
- abstractcore/providers/anthropic_provider.py +95 -8
- abstractcore/providers/base.py +1516 -81
- abstractcore/providers/huggingface_provider.py +546 -69
- abstractcore/providers/lmstudio_provider.py +30 -916
- abstractcore/providers/mlx_provider.py +382 -35
- abstractcore/providers/model_capabilities.py +5 -1
- abstractcore/providers/ollama_provider.py +99 -15
- abstractcore/providers/openai_compatible_provider.py +406 -180
- abstractcore/providers/openai_provider.py +188 -44
- abstractcore/providers/openrouter_provider.py +76 -0
- abstractcore/providers/registry.py +61 -5
- abstractcore/providers/streaming.py +138 -33
- abstractcore/providers/vllm_provider.py +92 -817
- abstractcore/server/app.py +478 -28
- abstractcore/server/audio_endpoints.py +139 -0
- abstractcore/server/vision_endpoints.py +1319 -0
- abstractcore/structured/handler.py +316 -41
- abstractcore/tools/common_tools.py +5501 -2012
- abstractcore/tools/comms_tools.py +1641 -0
- abstractcore/tools/core.py +37 -7
- abstractcore/tools/handler.py +4 -9
- abstractcore/tools/parser.py +49 -2
- abstractcore/tools/tag_rewriter.py +2 -1
- abstractcore/tools/telegram_tdlib.py +407 -0
- abstractcore/tools/telegram_tools.py +261 -0
- abstractcore/utils/cli.py +1085 -72
- abstractcore/utils/structured_logging.py +29 -8
- abstractcore/utils/token_utils.py +2 -0
- abstractcore/utils/truncation.py +29 -0
- abstractcore/utils/version.py +3 -4
- abstractcore/utils/vlm_token_calculator.py +12 -2
- abstractcore-2.11.4.dist-info/METADATA +562 -0
- abstractcore-2.11.4.dist-info/RECORD +133 -0
- {abstractcore-2.9.1.dist-info → abstractcore-2.11.4.dist-info}/WHEEL +1 -1
- {abstractcore-2.9.1.dist-info → abstractcore-2.11.4.dist-info}/entry_points.txt +1 -0
- abstractcore-2.9.1.dist-info/METADATA +0 -1190
- abstractcore-2.9.1.dist-info/RECORD +0 -119
- {abstractcore-2.9.1.dist-info → abstractcore-2.11.4.dist-info}/licenses/LICENSE +0 -0
- {abstractcore-2.9.1.dist-info → abstractcore-2.11.4.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"""
|
|
2
|
+
OpenAI-compatible audio endpoints for AbstractCore Server.
|
|
3
|
+
|
|
4
|
+
These endpoints are intentionally dependency-light:
|
|
5
|
+
- They do not require an LLM provider configuration.
|
|
6
|
+
- They delegate to AbstractCore capability plugins (e.g. AbstractVoice).
|
|
7
|
+
|
|
8
|
+
Endpoints:
|
|
9
|
+
- POST /v1/audio/transcriptions (multipart; STT)
|
|
10
|
+
- POST /v1/audio/speech (json; TTS)
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import threading
|
|
16
|
+
from typing import Any, Dict, Optional
|
|
17
|
+
|
|
18
|
+
from fastapi import APIRouter, HTTPException, Request
|
|
19
|
+
from fastapi.responses import Response
|
|
20
|
+
|
|
21
|
+
from ..capabilities.errors import CapabilityUnavailableError
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
router = APIRouter(tags=["audio"])
|
|
25
|
+
|
|
26
|
+
_CORE_LOCK = threading.Lock()
|
|
27
|
+
_CORE: Optional[Any] = None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _get_capability_core() -> Any:
|
|
31
|
+
"""Create/reuse a tiny AbstractCoreInterface host for capability plugins."""
|
|
32
|
+
global _CORE
|
|
33
|
+
with _CORE_LOCK:
|
|
34
|
+
if _CORE is not None:
|
|
35
|
+
return _CORE
|
|
36
|
+
|
|
37
|
+
from ..core.interface import AbstractCoreInterface
|
|
38
|
+
from ..core.types import GenerateResponse
|
|
39
|
+
|
|
40
|
+
class _CapabilityOnlyCore(AbstractCoreInterface):
|
|
41
|
+
def generate(self, prompt: str, **kwargs): # type: ignore[override]
|
|
42
|
+
_ = kwargs
|
|
43
|
+
return GenerateResponse(content=str(prompt))
|
|
44
|
+
|
|
45
|
+
def get_capabilities(self): # type: ignore[override]
|
|
46
|
+
return []
|
|
47
|
+
|
|
48
|
+
def unload_model(self, model_name: str) -> None: # type: ignore[override]
|
|
49
|
+
_ = model_name
|
|
50
|
+
return None
|
|
51
|
+
|
|
52
|
+
_CORE = _CapabilityOnlyCore(model="capabilities")
|
|
53
|
+
return _CORE
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _require_dict(data: Any, *, where: str) -> Dict[str, Any]:
|
|
57
|
+
if not isinstance(data, dict):
|
|
58
|
+
raise HTTPException(status_code=422, detail=f"Invalid request body for {where}: expected a JSON object.")
|
|
59
|
+
return data
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@router.post("/audio/transcriptions")
|
|
63
|
+
async def audio_transcriptions(request: Request):
|
|
64
|
+
"""OpenAI-compatible STT endpoint (multipart form with file)."""
|
|
65
|
+
try:
|
|
66
|
+
form = await request.form()
|
|
67
|
+
except Exception as e:
|
|
68
|
+
raise HTTPException(
|
|
69
|
+
status_code=501,
|
|
70
|
+
detail='python-multipart is required for /v1/audio/transcriptions. Install with: pip install "python-multipart".',
|
|
71
|
+
) from e
|
|
72
|
+
|
|
73
|
+
file_obj = form.get("file")
|
|
74
|
+
if file_obj is None:
|
|
75
|
+
raise HTTPException(status_code=422, detail="Missing required form field: file")
|
|
76
|
+
|
|
77
|
+
# Starlette's UploadFile provides .read() coroutine + filename.
|
|
78
|
+
read = getattr(file_obj, "read", None)
|
|
79
|
+
if not callable(read):
|
|
80
|
+
raise HTTPException(status_code=422, detail="Invalid form field 'file': expected an uploaded file.")
|
|
81
|
+
|
|
82
|
+
try:
|
|
83
|
+
audio_bytes = await read()
|
|
84
|
+
except Exception as e:
|
|
85
|
+
raise HTTPException(status_code=400, detail=f"Failed to read uploaded audio file: {e}") from e
|
|
86
|
+
|
|
87
|
+
language = form.get("language")
|
|
88
|
+
language = str(language).strip() if isinstance(language, str) and language.strip() else None
|
|
89
|
+
|
|
90
|
+
core = _get_capability_core()
|
|
91
|
+
try:
|
|
92
|
+
text = core.audio.transcribe(bytes(audio_bytes), language=language)
|
|
93
|
+
except CapabilityUnavailableError as e:
|
|
94
|
+
raise HTTPException(status_code=501, detail=str(e)) from e
|
|
95
|
+
except Exception as e:
|
|
96
|
+
raise HTTPException(status_code=500, detail=f"Audio transcription failed: {e}") from e
|
|
97
|
+
|
|
98
|
+
return {"text": str(text or "").strip()}
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@router.post("/audio/speech")
|
|
102
|
+
async def audio_speech(request: Request):
|
|
103
|
+
"""OpenAI-compatible TTS endpoint (json with input text)."""
|
|
104
|
+
try:
|
|
105
|
+
payload = await request.json()
|
|
106
|
+
except Exception as e:
|
|
107
|
+
raise HTTPException(status_code=422, detail=f"Invalid JSON body: {e}") from e
|
|
108
|
+
|
|
109
|
+
data = _require_dict(payload, where="/v1/audio/speech")
|
|
110
|
+
|
|
111
|
+
input_text = data.get("input")
|
|
112
|
+
if input_text is None:
|
|
113
|
+
input_text = data.get("text")
|
|
114
|
+
if not isinstance(input_text, str) or not input_text.strip():
|
|
115
|
+
raise HTTPException(status_code=422, detail="Missing required field: input (string)")
|
|
116
|
+
|
|
117
|
+
voice = data.get("voice")
|
|
118
|
+
voice = str(voice).strip() if isinstance(voice, str) and voice.strip() else None
|
|
119
|
+
|
|
120
|
+
fmt = data.get("format")
|
|
121
|
+
if fmt is None:
|
|
122
|
+
fmt = data.get("response_format")
|
|
123
|
+
fmt = str(fmt).strip().lower() if isinstance(fmt, str) and fmt.strip() else "wav"
|
|
124
|
+
|
|
125
|
+
core = _get_capability_core()
|
|
126
|
+
try:
|
|
127
|
+
audio = core.voice.tts(str(input_text), voice=voice, format=fmt)
|
|
128
|
+
except CapabilityUnavailableError as e:
|
|
129
|
+
raise HTTPException(status_code=501, detail=str(e)) from e
|
|
130
|
+
except Exception as e:
|
|
131
|
+
raise HTTPException(status_code=500, detail=f"Audio synthesis failed: {e}") from e
|
|
132
|
+
|
|
133
|
+
if not isinstance(audio, (bytes, bytearray)):
|
|
134
|
+
raise HTTPException(
|
|
135
|
+
status_code=500,
|
|
136
|
+
detail="TTS backend returned an unexpected type (expected raw bytes).",
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
return Response(content=bytes(audio), media_type=f"audio/{fmt}")
|