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.
Files changed (85) hide show
  1. abstractcore/__init__.py +7 -27
  2. abstractcore/apps/deepsearch.py +9 -4
  3. abstractcore/apps/extractor.py +33 -100
  4. abstractcore/apps/intent.py +19 -0
  5. abstractcore/apps/judge.py +20 -1
  6. abstractcore/apps/summarizer.py +20 -1
  7. abstractcore/architectures/detection.py +34 -1
  8. abstractcore/architectures/response_postprocessing.py +313 -0
  9. abstractcore/assets/architecture_formats.json +38 -8
  10. abstractcore/assets/model_capabilities.json +882 -160
  11. abstractcore/compression/__init__.py +1 -2
  12. abstractcore/compression/glyph_processor.py +6 -4
  13. abstractcore/config/main.py +52 -20
  14. abstractcore/config/manager.py +390 -12
  15. abstractcore/config/vision_config.py +5 -5
  16. abstractcore/core/interface.py +151 -3
  17. abstractcore/core/session.py +16 -10
  18. abstractcore/download.py +1 -1
  19. abstractcore/embeddings/manager.py +20 -6
  20. abstractcore/endpoint/__init__.py +2 -0
  21. abstractcore/endpoint/app.py +458 -0
  22. abstractcore/mcp/client.py +3 -1
  23. abstractcore/media/__init__.py +52 -17
  24. abstractcore/media/auto_handler.py +42 -22
  25. abstractcore/media/base.py +44 -1
  26. abstractcore/media/capabilities.py +12 -33
  27. abstractcore/media/enrichment.py +105 -0
  28. abstractcore/media/handlers/anthropic_handler.py +19 -28
  29. abstractcore/media/handlers/local_handler.py +124 -70
  30. abstractcore/media/handlers/openai_handler.py +19 -31
  31. abstractcore/media/processors/__init__.py +4 -2
  32. abstractcore/media/processors/audio_processor.py +57 -0
  33. abstractcore/media/processors/office_processor.py +8 -3
  34. abstractcore/media/processors/pdf_processor.py +46 -3
  35. abstractcore/media/processors/text_processor.py +22 -24
  36. abstractcore/media/processors/video_processor.py +58 -0
  37. abstractcore/media/types.py +97 -4
  38. abstractcore/media/utils/image_scaler.py +20 -2
  39. abstractcore/media/utils/video_frames.py +219 -0
  40. abstractcore/media/vision_fallback.py +136 -22
  41. abstractcore/processing/__init__.py +32 -3
  42. abstractcore/processing/basic_deepsearch.py +15 -10
  43. abstractcore/processing/basic_intent.py +3 -2
  44. abstractcore/processing/basic_judge.py +3 -2
  45. abstractcore/processing/basic_summarizer.py +1 -1
  46. abstractcore/providers/__init__.py +3 -1
  47. abstractcore/providers/anthropic_provider.py +95 -8
  48. abstractcore/providers/base.py +1516 -81
  49. abstractcore/providers/huggingface_provider.py +546 -69
  50. abstractcore/providers/lmstudio_provider.py +30 -916
  51. abstractcore/providers/mlx_provider.py +382 -35
  52. abstractcore/providers/model_capabilities.py +5 -1
  53. abstractcore/providers/ollama_provider.py +99 -15
  54. abstractcore/providers/openai_compatible_provider.py +406 -180
  55. abstractcore/providers/openai_provider.py +188 -44
  56. abstractcore/providers/openrouter_provider.py +76 -0
  57. abstractcore/providers/registry.py +61 -5
  58. abstractcore/providers/streaming.py +138 -33
  59. abstractcore/providers/vllm_provider.py +92 -817
  60. abstractcore/server/app.py +478 -28
  61. abstractcore/server/audio_endpoints.py +139 -0
  62. abstractcore/server/vision_endpoints.py +1319 -0
  63. abstractcore/structured/handler.py +316 -41
  64. abstractcore/tools/common_tools.py +5501 -2012
  65. abstractcore/tools/comms_tools.py +1641 -0
  66. abstractcore/tools/core.py +37 -7
  67. abstractcore/tools/handler.py +4 -9
  68. abstractcore/tools/parser.py +49 -2
  69. abstractcore/tools/tag_rewriter.py +2 -1
  70. abstractcore/tools/telegram_tdlib.py +407 -0
  71. abstractcore/tools/telegram_tools.py +261 -0
  72. abstractcore/utils/cli.py +1085 -72
  73. abstractcore/utils/structured_logging.py +29 -8
  74. abstractcore/utils/token_utils.py +2 -0
  75. abstractcore/utils/truncation.py +29 -0
  76. abstractcore/utils/version.py +3 -4
  77. abstractcore/utils/vlm_token_calculator.py +12 -2
  78. abstractcore-2.11.4.dist-info/METADATA +562 -0
  79. abstractcore-2.11.4.dist-info/RECORD +133 -0
  80. {abstractcore-2.9.1.dist-info → abstractcore-2.11.4.dist-info}/WHEEL +1 -1
  81. {abstractcore-2.9.1.dist-info → abstractcore-2.11.4.dist-info}/entry_points.txt +1 -0
  82. abstractcore-2.9.1.dist-info/METADATA +0 -1190
  83. abstractcore-2.9.1.dist-info/RECORD +0 -119
  84. {abstractcore-2.9.1.dist-info → abstractcore-2.11.4.dist-info}/licenses/LICENSE +0 -0
  85. {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}")