abstractcore 2.4.6__py3-none-any.whl → 2.4.8__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 +5 -1
- abstractcore/assets/session_schema.json +1 -1
- abstractcore/core/factory.py +1 -1
- abstractcore/core/session.py +1 -1
- abstractcore/core/types.py +25 -1
- abstractcore/providers/__init__.py +0 -2
- abstractcore/providers/anthropic_provider.py +6 -0
- abstractcore/providers/huggingface_provider.py +21 -9
- abstractcore/providers/lmstudio_provider.py +11 -3
- abstractcore/providers/mlx_provider.py +16 -7
- abstractcore/providers/ollama_provider.py +10 -3
- abstractcore/providers/openai_provider.py +12 -3
- abstractcore/providers/registry.py +1 -17
- abstractcore/server/app.py +0 -1
- abstractcore/utils/version.py +1 -1
- {abstractcore-2.4.6.dist-info → abstractcore-2.4.8.dist-info}/METADATA +50 -4
- {abstractcore-2.4.6.dist-info → abstractcore-2.4.8.dist-info}/RECORD +21 -22
- abstractcore/providers/mock_provider.py +0 -157
- {abstractcore-2.4.6.dist-info → abstractcore-2.4.8.dist-info}/WHEEL +0 -0
- {abstractcore-2.4.6.dist-info → abstractcore-2.4.8.dist-info}/entry_points.txt +0 -0
- {abstractcore-2.4.6.dist-info → abstractcore-2.4.8.dist-info}/licenses/LICENSE +0 -0
- {abstractcore-2.4.6.dist-info → abstractcore-2.4.8.dist-info}/top_level.txt +0 -0
abstractcore/__init__.py
CHANGED
|
@@ -44,6 +44,9 @@ except ImportError:
|
|
|
44
44
|
from .processing import BasicSummarizer, SummaryStyle, SummaryLength, BasicExtractor
|
|
45
45
|
_has_processing = True
|
|
46
46
|
|
|
47
|
+
# Tools module (core functionality)
|
|
48
|
+
from .tools import tool
|
|
49
|
+
|
|
47
50
|
__all__ = [
|
|
48
51
|
'create_llm',
|
|
49
52
|
'BasicSession',
|
|
@@ -54,7 +57,8 @@ __all__ = [
|
|
|
54
57
|
'MessageRole',
|
|
55
58
|
'ModelNotFoundError',
|
|
56
59
|
'ProviderAPIError',
|
|
57
|
-
'AuthenticationError'
|
|
60
|
+
'AuthenticationError',
|
|
61
|
+
'tool'
|
|
58
62
|
]
|
|
59
63
|
|
|
60
64
|
if _has_embeddings:
|
abstractcore/core/factory.py
CHANGED
|
@@ -12,7 +12,7 @@ def create_llm(provider: str, model: Optional[str] = None, **kwargs) -> Abstract
|
|
|
12
12
|
Create an LLM provider instance with unified token parameter support.
|
|
13
13
|
|
|
14
14
|
Args:
|
|
15
|
-
provider: Provider name (openai, anthropic, ollama, huggingface, mlx, lmstudio
|
|
15
|
+
provider: Provider name (openai, anthropic, ollama, huggingface, mlx, lmstudio)
|
|
16
16
|
model: Model name (optional, will use provider default)
|
|
17
17
|
**kwargs: Additional configuration including token parameters
|
|
18
18
|
|
abstractcore/core/session.py
CHANGED
|
@@ -760,7 +760,7 @@ class BasicSession:
|
|
|
760
760
|
"tokens_before": original_tokens,
|
|
761
761
|
"tokens_after": self._estimate_tokens_for_summary(summary_result.summary),
|
|
762
762
|
"compression_ratio": self._calculate_compression_ratio(original_tokens, summary_result.summary),
|
|
763
|
-
"
|
|
763
|
+
"gen_time": duration_ms
|
|
764
764
|
}
|
|
765
765
|
}
|
|
766
766
|
|
abstractcore/core/types.py
CHANGED
|
@@ -91,6 +91,7 @@ class GenerateResponse:
|
|
|
91
91
|
usage: Optional[Dict[str, int]] = None
|
|
92
92
|
tool_calls: Optional[List[Dict[str, Any]]] = None
|
|
93
93
|
metadata: Optional[Dict[str, Any]] = None
|
|
94
|
+
gen_time: Optional[float] = None # Generation time in milliseconds
|
|
94
95
|
|
|
95
96
|
def has_tool_calls(self) -> bool:
|
|
96
97
|
"""Check if response contains tool calls"""
|
|
@@ -109,6 +110,29 @@ class GenerateResponse:
|
|
|
109
110
|
parts.append(f"Model: {self.model}")
|
|
110
111
|
if self.usage:
|
|
111
112
|
parts.append(f"Tokens: {self.usage.get('total_tokens', 'unknown')}")
|
|
113
|
+
if self.gen_time:
|
|
114
|
+
parts.append(f"Time: {self.gen_time:.1f}ms")
|
|
112
115
|
if self.tool_calls:
|
|
113
116
|
parts.append(f"Tools: {len(self.tool_calls)} executed")
|
|
114
|
-
return " | ".join(parts)
|
|
117
|
+
return " | ".join(parts)
|
|
118
|
+
|
|
119
|
+
@property
|
|
120
|
+
def input_tokens(self) -> Optional[int]:
|
|
121
|
+
"""Get input tokens with consistent terminology (prompt_tokens or input_tokens)."""
|
|
122
|
+
if not self.usage:
|
|
123
|
+
return None
|
|
124
|
+
return self.usage.get('input_tokens') or self.usage.get('prompt_tokens')
|
|
125
|
+
|
|
126
|
+
@property
|
|
127
|
+
def output_tokens(self) -> Optional[int]:
|
|
128
|
+
"""Get output tokens with consistent terminology (completion_tokens or output_tokens)."""
|
|
129
|
+
if not self.usage:
|
|
130
|
+
return None
|
|
131
|
+
return self.usage.get('output_tokens') or self.usage.get('completion_tokens')
|
|
132
|
+
|
|
133
|
+
@property
|
|
134
|
+
def total_tokens(self) -> Optional[int]:
|
|
135
|
+
"""Get total tokens."""
|
|
136
|
+
if not self.usage:
|
|
137
|
+
return None
|
|
138
|
+
return self.usage.get('total_tokens')
|
|
@@ -7,7 +7,6 @@ from .ollama_provider import OllamaProvider
|
|
|
7
7
|
from .lmstudio_provider import LMStudioProvider
|
|
8
8
|
from .huggingface_provider import HuggingFaceProvider
|
|
9
9
|
from .mlx_provider import MLXProvider
|
|
10
|
-
from .mock_provider import MockProvider
|
|
11
10
|
|
|
12
11
|
# Provider registry for centralized provider discovery and management
|
|
13
12
|
from .registry import (
|
|
@@ -32,7 +31,6 @@ __all__ = [
|
|
|
32
31
|
'LMStudioProvider',
|
|
33
32
|
'HuggingFaceProvider',
|
|
34
33
|
'MLXProvider',
|
|
35
|
-
'MockProvider',
|
|
36
34
|
|
|
37
35
|
# Provider registry
|
|
38
36
|
'ProviderRegistry',
|
|
@@ -186,8 +186,14 @@ class AnthropicProvider(BaseProvider):
|
|
|
186
186
|
if stream:
|
|
187
187
|
return self._stream_response(call_params, tools)
|
|
188
188
|
else:
|
|
189
|
+
# Track generation time
|
|
190
|
+
start_time = time.time()
|
|
189
191
|
response = self.client.messages.create(**call_params)
|
|
192
|
+
gen_time = round((time.time() - start_time) * 1000, 1)
|
|
193
|
+
|
|
190
194
|
formatted = self._format_response(response)
|
|
195
|
+
# Add generation time to response
|
|
196
|
+
formatted.gen_time = gen_time
|
|
191
197
|
|
|
192
198
|
# Handle tool execution for Anthropic responses
|
|
193
199
|
if tools and (formatted.has_tool_calls() or
|
|
@@ -863,6 +863,9 @@ class HuggingFaceProvider(BaseProvider):
|
|
|
863
863
|
if torch.cuda.is_available():
|
|
864
864
|
torch.cuda.manual_seed_all(seed)
|
|
865
865
|
|
|
866
|
+
# Track generation time
|
|
867
|
+
start_time = time.time()
|
|
868
|
+
|
|
866
869
|
outputs = self.pipeline(
|
|
867
870
|
input_text,
|
|
868
871
|
max_new_tokens=max_new_tokens,
|
|
@@ -874,6 +877,8 @@ class HuggingFaceProvider(BaseProvider):
|
|
|
874
877
|
truncation=True,
|
|
875
878
|
return_full_text=False
|
|
876
879
|
)
|
|
880
|
+
|
|
881
|
+
gen_time = round((time.time() - start_time) * 1000, 1)
|
|
877
882
|
|
|
878
883
|
if outputs and len(outputs) > 0:
|
|
879
884
|
response_text = outputs[0]['generated_text'].strip()
|
|
@@ -885,34 +890,41 @@ class HuggingFaceProvider(BaseProvider):
|
|
|
885
890
|
content=response_text,
|
|
886
891
|
model=self.model,
|
|
887
892
|
finish_reason="stop",
|
|
888
|
-
usage=usage
|
|
893
|
+
usage=usage,
|
|
894
|
+
gen_time=gen_time
|
|
889
895
|
)
|
|
890
896
|
else:
|
|
891
897
|
return GenerateResponse(
|
|
892
898
|
content="",
|
|
893
899
|
model=self.model,
|
|
894
|
-
finish_reason="stop"
|
|
900
|
+
finish_reason="stop",
|
|
901
|
+
gen_time=gen_time
|
|
895
902
|
)
|
|
896
903
|
|
|
897
904
|
except Exception as e:
|
|
905
|
+
gen_time = round((time.time() - start_time) * 1000, 1) if 'start_time' in locals() else 0.0
|
|
898
906
|
return GenerateResponse(
|
|
899
907
|
content=f"Error: {str(e)}",
|
|
900
908
|
model=self.model,
|
|
901
|
-
finish_reason="error"
|
|
909
|
+
finish_reason="error",
|
|
910
|
+
gen_time=gen_time
|
|
902
911
|
)
|
|
903
912
|
|
|
904
913
|
def _calculate_usage(self, prompt: str, response: str) -> Dict[str, int]:
|
|
905
914
|
"""Calculate token usage using centralized token utilities."""
|
|
906
915
|
from ..utils.token_utils import TokenUtils
|
|
907
916
|
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
total_tokens =
|
|
917
|
+
input_tokens = TokenUtils.estimate_tokens(prompt, self.model)
|
|
918
|
+
output_tokens = TokenUtils.estimate_tokens(response, self.model)
|
|
919
|
+
total_tokens = input_tokens + output_tokens
|
|
911
920
|
|
|
912
921
|
return {
|
|
913
|
-
"
|
|
914
|
-
"
|
|
915
|
-
"total_tokens": total_tokens
|
|
922
|
+
"input_tokens": input_tokens,
|
|
923
|
+
"output_tokens": output_tokens,
|
|
924
|
+
"total_tokens": total_tokens,
|
|
925
|
+
# Keep legacy keys for backward compatibility
|
|
926
|
+
"prompt_tokens": input_tokens,
|
|
927
|
+
"completion_tokens": output_tokens
|
|
916
928
|
}
|
|
917
929
|
|
|
918
930
|
def _stream_generate_transformers(self, input_text: str, max_new_tokens: int,
|
|
@@ -4,6 +4,7 @@ LM Studio provider implementation (OpenAI-compatible API).
|
|
|
4
4
|
|
|
5
5
|
import httpx
|
|
6
6
|
import json
|
|
7
|
+
import time
|
|
7
8
|
from typing import List, Dict, Any, Optional, Union, Iterator, Type
|
|
8
9
|
|
|
9
10
|
try:
|
|
@@ -225,12 +226,15 @@ class LMStudioProvider(BaseProvider):
|
|
|
225
226
|
if not hasattr(self, 'client') or self.client is None:
|
|
226
227
|
raise ProviderAPIError("HTTP client not initialized")
|
|
227
228
|
|
|
229
|
+
# Track generation time
|
|
230
|
+
start_time = time.time()
|
|
228
231
|
response = self.client.post(
|
|
229
232
|
f"{self.base_url}/chat/completions",
|
|
230
233
|
json=payload,
|
|
231
234
|
headers={"Content-Type": "application/json"}
|
|
232
235
|
)
|
|
233
236
|
response.raise_for_status()
|
|
237
|
+
gen_time = round((time.time() - start_time) * 1000, 1)
|
|
234
238
|
|
|
235
239
|
result = response.json()
|
|
236
240
|
|
|
@@ -252,10 +256,14 @@ class LMStudioProvider(BaseProvider):
|
|
|
252
256
|
finish_reason=finish_reason,
|
|
253
257
|
raw_response=result,
|
|
254
258
|
usage={
|
|
259
|
+
"input_tokens": usage.get("prompt_tokens", 0),
|
|
260
|
+
"output_tokens": usage.get("completion_tokens", 0),
|
|
261
|
+
"total_tokens": usage.get("total_tokens", 0),
|
|
262
|
+
# Keep legacy keys for backward compatibility
|
|
255
263
|
"prompt_tokens": usage.get("prompt_tokens", 0),
|
|
256
|
-
"completion_tokens": usage.get("completion_tokens", 0)
|
|
257
|
-
|
|
258
|
-
|
|
264
|
+
"completion_tokens": usage.get("completion_tokens", 0)
|
|
265
|
+
},
|
|
266
|
+
gen_time=gen_time
|
|
259
267
|
)
|
|
260
268
|
|
|
261
269
|
except AttributeError as e:
|
|
@@ -266,6 +266,9 @@ class MLXProvider(BaseProvider):
|
|
|
266
266
|
mx.random.seed(seed)
|
|
267
267
|
self.logger.debug(f"Set MLX random seed to {seed} for deterministic generation")
|
|
268
268
|
|
|
269
|
+
# Track generation time
|
|
270
|
+
start_time = time.time()
|
|
271
|
+
|
|
269
272
|
# Try different MLX API signatures
|
|
270
273
|
try:
|
|
271
274
|
# Try new mlx-lm API
|
|
@@ -288,6 +291,8 @@ class MLXProvider(BaseProvider):
|
|
|
288
291
|
# Fallback to basic response
|
|
289
292
|
response_text = prompt + " I am an AI assistant powered by MLX on Apple Silicon."
|
|
290
293
|
|
|
294
|
+
gen_time = round((time.time() - start_time) * 1000, 1)
|
|
295
|
+
|
|
291
296
|
# Use the full response as-is - preserve all content including thinking
|
|
292
297
|
generated = response_text.strip()
|
|
293
298
|
|
|
@@ -295,21 +300,25 @@ class MLXProvider(BaseProvider):
|
|
|
295
300
|
content=generated,
|
|
296
301
|
model=self.model,
|
|
297
302
|
finish_reason="stop",
|
|
298
|
-
usage=self._calculate_usage(prompt, generated)
|
|
303
|
+
usage=self._calculate_usage(prompt, generated),
|
|
304
|
+
gen_time=gen_time
|
|
299
305
|
)
|
|
300
306
|
|
|
301
307
|
def _calculate_usage(self, prompt: str, response: str) -> Dict[str, int]:
|
|
302
308
|
"""Calculate token usage using centralized token utilities."""
|
|
303
309
|
from ..utils.token_utils import TokenUtils
|
|
304
310
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
total_tokens =
|
|
311
|
+
input_tokens = TokenUtils.estimate_tokens(prompt, self.model)
|
|
312
|
+
output_tokens = TokenUtils.estimate_tokens(response, self.model)
|
|
313
|
+
total_tokens = input_tokens + output_tokens
|
|
308
314
|
|
|
309
315
|
return {
|
|
310
|
-
"
|
|
311
|
-
"
|
|
312
|
-
"total_tokens": total_tokens
|
|
316
|
+
"input_tokens": input_tokens,
|
|
317
|
+
"output_tokens": output_tokens,
|
|
318
|
+
"total_tokens": total_tokens,
|
|
319
|
+
# Keep legacy keys for backward compatibility
|
|
320
|
+
"prompt_tokens": input_tokens,
|
|
321
|
+
"completion_tokens": output_tokens
|
|
313
322
|
}
|
|
314
323
|
|
|
315
324
|
def _stream_generate(self, prompt: str, max_tokens: int, temperature: float, top_p: float, tool_call_tags: Optional[str] = None, seed: Optional[int] = None) -> Iterator[GenerateResponse]:
|
|
@@ -225,11 +225,14 @@ class OllamaProvider(BaseProvider):
|
|
|
225
225
|
def _single_generate(self, endpoint: str, payload: Dict[str, Any], tools: Optional[List[Dict[str, Any]]] = None) -> GenerateResponse:
|
|
226
226
|
"""Generate single response"""
|
|
227
227
|
try:
|
|
228
|
+
# Track generation time
|
|
229
|
+
start_time = time.time()
|
|
228
230
|
response = self.client.post(
|
|
229
231
|
f"{self.base_url}{endpoint}",
|
|
230
232
|
json=payload
|
|
231
233
|
)
|
|
232
234
|
response.raise_for_status()
|
|
235
|
+
gen_time = round((time.time() - start_time) * 1000, 1)
|
|
233
236
|
|
|
234
237
|
result = response.json()
|
|
235
238
|
|
|
@@ -246,10 +249,14 @@ class OllamaProvider(BaseProvider):
|
|
|
246
249
|
finish_reason="stop",
|
|
247
250
|
raw_response=result,
|
|
248
251
|
usage={
|
|
252
|
+
"input_tokens": result.get("prompt_eval_count", 0),
|
|
253
|
+
"output_tokens": result.get("eval_count", 0),
|
|
254
|
+
"total_tokens": result.get("prompt_eval_count", 0) + result.get("eval_count", 0),
|
|
255
|
+
# Keep legacy keys for backward compatibility
|
|
249
256
|
"prompt_tokens": result.get("prompt_eval_count", 0),
|
|
250
|
-
"completion_tokens": result.get("eval_count", 0)
|
|
251
|
-
|
|
252
|
-
|
|
257
|
+
"completion_tokens": result.get("eval_count", 0)
|
|
258
|
+
},
|
|
259
|
+
gen_time=gen_time
|
|
253
260
|
)
|
|
254
261
|
|
|
255
262
|
# Execute tools if enabled and tools are present
|
|
@@ -169,8 +169,14 @@ class OpenAIProvider(BaseProvider):
|
|
|
169
169
|
if stream:
|
|
170
170
|
return self._stream_response(call_params, tools)
|
|
171
171
|
else:
|
|
172
|
+
# Track generation time
|
|
173
|
+
start_time = time.time()
|
|
172
174
|
response = self.client.chat.completions.create(**call_params)
|
|
175
|
+
gen_time = round((time.time() - start_time) * 1000, 1)
|
|
176
|
+
|
|
173
177
|
formatted = self._format_response(response)
|
|
178
|
+
# Add generation time to response
|
|
179
|
+
formatted.gen_time = gen_time
|
|
174
180
|
|
|
175
181
|
# Handle tool execution for OpenAI native responses
|
|
176
182
|
if tools and formatted.has_tool_calls():
|
|
@@ -216,13 +222,16 @@ class OpenAIProvider(BaseProvider):
|
|
|
216
222
|
"arguments": tc.function.arguments
|
|
217
223
|
})
|
|
218
224
|
|
|
219
|
-
# Build usage dict with
|
|
225
|
+
# Build usage dict with consistent terminology
|
|
220
226
|
usage = None
|
|
221
227
|
if hasattr(response, 'usage'):
|
|
222
228
|
usage = {
|
|
229
|
+
"input_tokens": response.usage.prompt_tokens,
|
|
230
|
+
"output_tokens": response.usage.completion_tokens,
|
|
231
|
+
"total_tokens": response.usage.total_tokens,
|
|
232
|
+
# Keep legacy keys for backward compatibility
|
|
223
233
|
"prompt_tokens": response.usage.prompt_tokens,
|
|
224
|
-
"completion_tokens": response.usage.completion_tokens
|
|
225
|
-
"total_tokens": response.usage.total_tokens
|
|
234
|
+
"completion_tokens": response.usage.completion_tokens
|
|
226
235
|
}
|
|
227
236
|
|
|
228
237
|
# Add detailed token breakdown for reasoning models
|
|
@@ -136,19 +136,6 @@ class ProviderRegistry:
|
|
|
136
136
|
import_path="..providers.huggingface_provider"
|
|
137
137
|
))
|
|
138
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
139
|
|
|
153
140
|
def register_provider(self, provider_info: ProviderInfo):
|
|
154
141
|
"""Register a provider in the registry."""
|
|
@@ -182,10 +169,7 @@ class ProviderRegistry:
|
|
|
182
169
|
def _load_provider_class(self, provider_info: ProviderInfo):
|
|
183
170
|
"""Dynamically load a provider class."""
|
|
184
171
|
try:
|
|
185
|
-
if provider_info.name == "
|
|
186
|
-
from ..providers.mock_provider import MockProvider
|
|
187
|
-
return MockProvider
|
|
188
|
-
elif provider_info.name == "openai":
|
|
172
|
+
if provider_info.name == "openai":
|
|
189
173
|
from ..providers.openai_provider import OpenAIProvider
|
|
190
174
|
return OpenAIProvider
|
|
191
175
|
elif provider_info.name == "anthropic":
|
abstractcore/server/app.py
CHANGED
|
@@ -1101,7 +1101,6 @@ async def list_providers():
|
|
|
1101
1101
|
- **LMStudio**: Local model development and testing platform
|
|
1102
1102
|
- **MLX**: Apple Silicon optimized local inference
|
|
1103
1103
|
- **HuggingFace**: Access to HuggingFace models (transformers and embeddings)
|
|
1104
|
-
- **Mock**: Testing provider for development
|
|
1105
1104
|
|
|
1106
1105
|
**Use Cases:**
|
|
1107
1106
|
- Discover available providers before making requests
|
abstractcore/utils/version.py
CHANGED
|
@@ -11,4 +11,4 @@ including when the package is installed from PyPI where pyproject.toml is not av
|
|
|
11
11
|
|
|
12
12
|
# Package version - update this when releasing new versions
|
|
13
13
|
# This must be manually synchronized with the version in pyproject.toml
|
|
14
|
-
__version__ = "2.4.
|
|
14
|
+
__version__ = "2.4.8"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: abstractcore
|
|
3
|
-
Version: 2.4.
|
|
3
|
+
Version: 2.4.8
|
|
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>
|
|
@@ -29,6 +29,7 @@ License-File: LICENSE
|
|
|
29
29
|
Requires-Dist: pydantic<3.0.0,>=2.0.0
|
|
30
30
|
Requires-Dist: httpx<1.0.0,>=0.24.0
|
|
31
31
|
Requires-Dist: tiktoken<1.0.0,>=0.5.0
|
|
32
|
+
Requires-Dist: requests<3.0.0,>=2.25.0
|
|
32
33
|
Provides-Extra: openai
|
|
33
34
|
Requires-Dist: openai<2.0.0,>=1.0.0; extra == "openai"
|
|
34
35
|
Provides-Extra: anthropic
|
|
@@ -46,6 +47,11 @@ Provides-Extra: embeddings
|
|
|
46
47
|
Requires-Dist: sentence-transformers<4.0.0,>=2.7.0; extra == "embeddings"
|
|
47
48
|
Requires-Dist: numpy<2.0.0,>=1.20.0; extra == "embeddings"
|
|
48
49
|
Provides-Extra: processing
|
|
50
|
+
Provides-Extra: tools
|
|
51
|
+
Requires-Dist: beautifulsoup4<5.0.0,>=4.12.0; extra == "tools"
|
|
52
|
+
Requires-Dist: lxml<6.0.0,>=4.9.0; extra == "tools"
|
|
53
|
+
Requires-Dist: duckduckgo-search<4.0.0,>=3.8.0; extra == "tools"
|
|
54
|
+
Requires-Dist: psutil<6.0.0,>=5.9.0; extra == "tools"
|
|
49
55
|
Provides-Extra: media
|
|
50
56
|
Requires-Dist: Pillow<12.0.0,>=10.0.0; extra == "media"
|
|
51
57
|
Requires-Dist: pymupdf4llm<1.0.0,>=0.0.20; extra == "media"
|
|
@@ -60,9 +66,9 @@ Requires-Dist: abstractcore[huggingface]; extra == "heavy-providers"
|
|
|
60
66
|
Provides-Extra: all-providers
|
|
61
67
|
Requires-Dist: abstractcore[anthropic,embeddings,huggingface,lmstudio,mlx,ollama,openai]; extra == "all-providers"
|
|
62
68
|
Provides-Extra: all
|
|
63
|
-
Requires-Dist: abstractcore[anthropic,dev,docs,embeddings,huggingface,lmstudio,media,mlx,ollama,openai,processing,server,test]; extra == "all"
|
|
69
|
+
Requires-Dist: abstractcore[anthropic,dev,docs,embeddings,huggingface,lmstudio,media,mlx,ollama,openai,processing,server,test,tools]; extra == "all"
|
|
64
70
|
Provides-Extra: lightweight
|
|
65
|
-
Requires-Dist: abstractcore[anthropic,embeddings,lmstudio,media,ollama,openai,processing,server]; extra == "lightweight"
|
|
71
|
+
Requires-Dist: abstractcore[anthropic,embeddings,lmstudio,media,ollama,openai,processing,server,tools]; extra == "lightweight"
|
|
66
72
|
Provides-Extra: dev
|
|
67
73
|
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
68
74
|
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
@@ -89,7 +95,7 @@ Requires-Dist: mkdocs-material>=9.0.0; extra == "docs"
|
|
|
89
95
|
Requires-Dist: mkdocstrings[python]>=0.22.0; extra == "docs"
|
|
90
96
|
Requires-Dist: mkdocs-autorefs>=0.4.0; extra == "docs"
|
|
91
97
|
Provides-Extra: full-dev
|
|
92
|
-
Requires-Dist: abstractcore[all-providers,dev,docs,test]; extra == "full-dev"
|
|
98
|
+
Requires-Dist: abstractcore[all-providers,dev,docs,test,tools]; extra == "full-dev"
|
|
93
99
|
Dynamic: license-file
|
|
94
100
|
|
|
95
101
|
# AbstractCore
|
|
@@ -155,6 +161,45 @@ response = llm.generate(
|
|
|
155
161
|
print(response.content)
|
|
156
162
|
```
|
|
157
163
|
|
|
164
|
+
### Response Object (GenerateResponse)
|
|
165
|
+
|
|
166
|
+
Every LLM generation returns a **GenerateResponse** object with consistent structure across all providers:
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
from abstractcore import create_llm
|
|
170
|
+
|
|
171
|
+
llm = create_llm("openai", model="gpt-4o-mini")
|
|
172
|
+
response = llm.generate("Explain quantum computing in simple terms")
|
|
173
|
+
|
|
174
|
+
# Core response data
|
|
175
|
+
print(f"Content: {response.content}") # Generated text
|
|
176
|
+
print(f"Model: {response.model}") # Model used
|
|
177
|
+
print(f"Finish reason: {response.finish_reason}") # Why generation stopped
|
|
178
|
+
|
|
179
|
+
# Consistent token access across ALL providers (NEW in v2.4.7)
|
|
180
|
+
print(f"Input tokens: {response.input_tokens}") # Always available
|
|
181
|
+
print(f"Output tokens: {response.output_tokens}") # Always available
|
|
182
|
+
print(f"Total tokens: {response.total_tokens}") # Always available
|
|
183
|
+
|
|
184
|
+
# Generation time tracking (NEW in v2.4.7)
|
|
185
|
+
print(f"Generation time: {response.gen_time}ms") # Always available (rounded to 1 decimal)
|
|
186
|
+
|
|
187
|
+
# Advanced access
|
|
188
|
+
print(f"Tool calls: {response.tool_calls}") # Tools executed (if any)
|
|
189
|
+
print(f"Raw usage: {response.usage}") # Provider-specific token data
|
|
190
|
+
print(f"Metadata: {response.metadata}") # Additional context
|
|
191
|
+
|
|
192
|
+
# Comprehensive summary
|
|
193
|
+
print(f"Summary: {response.get_summary()}") # "Model: gpt-4o-mini | Tokens: 117 | Time: 1234.5ms"
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
**Token Count Sources:**
|
|
197
|
+
- **Provider APIs**: OpenAI, Anthropic, LMStudio (native API token counts)
|
|
198
|
+
- **AbstractCore Calculation**: MLX, HuggingFace (using `token_utils.py`)
|
|
199
|
+
- **Mixed Sources**: Ollama (combination of provider and calculated tokens)
|
|
200
|
+
|
|
201
|
+
**Backward Compatibility**: Legacy `prompt_tokens` and `completion_tokens` keys remain available in `response.usage` dictionary.
|
|
202
|
+
|
|
158
203
|
### Built-in Tools
|
|
159
204
|
|
|
160
205
|
AbstractCore includes a comprehensive set of ready-to-use tools for common tasks:
|
|
@@ -271,6 +316,7 @@ response = llm.generate(
|
|
|
271
316
|
- **Session Management**: Persistent conversations with metadata, analytics, and complete serialization
|
|
272
317
|
- **Structured Responses**: Clean, predictable output formats with Pydantic
|
|
273
318
|
- **Streaming Support**: Real-time token generation for interactive experiences
|
|
319
|
+
- **Consistent Token Terminology**: Unified `input_tokens`, `output_tokens`, `total_tokens` across all providers
|
|
274
320
|
- **Embeddings**: Built-in support for semantic search and RAG applications
|
|
275
321
|
- **Universal Server**: Optional OpenAI-compatible API server with `/v1/responses` endpoint
|
|
276
322
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
abstractcore/__init__.py,sha256=
|
|
1
|
+
abstractcore/__init__.py,sha256=A7Gbn_C-robdWLLQXjtTyWsoaXDGIblSFmLRV_ni6O8,1934
|
|
2
2
|
abstractcore/apps/__init__.py,sha256=sgNOv3lYyOWNBC-w6GnRN6aILGCbdkQtNcuQdJz5ghE,31
|
|
3
3
|
abstractcore/apps/__main__.py,sha256=041daYkoIE1VLEO19IZC5nNIDOQZWNgpB7ogJ3SOlsE,1585
|
|
4
4
|
abstractcore/apps/app_config_utils.py,sha256=GygjtkianBktRvirHya1KbXuB2r5OUDJM6ilvybQWvk,875
|
|
@@ -10,17 +10,17 @@ abstractcore/architectures/detection.py,sha256=Cxap1pL6qOJjyY22Vc4hzbLALTuuBisPT
|
|
|
10
10
|
abstractcore/architectures/enums.py,sha256=9vIv2vDBEKhxwzwH9iaSAyf-iVj3p8y9loMeN_mYTJ8,3821
|
|
11
11
|
abstractcore/assets/architecture_formats.json,sha256=CIf6SaR_IJs1D7Uvd1K3zWngIXJ_yq3DM_IE3wnpCHY,16076
|
|
12
12
|
abstractcore/assets/model_capabilities.json,sha256=iUkDiljyZUZzPlpYCOFgStXyc6e7dvOfReYQ0HFrX9Q,49703
|
|
13
|
-
abstractcore/assets/session_schema.json,sha256=
|
|
13
|
+
abstractcore/assets/session_schema.json,sha256=hMCVrq3KSyVExrMGzuykf7bU-z6WyIVuEGU8du9_zUY,10570
|
|
14
14
|
abstractcore/cli/__init__.py,sha256=rUjLjZSK3wENSw4g_iN43Bc2i5cggcEmj4NPXBMohdc,241
|
|
15
15
|
abstractcore/cli/main.py,sha256=QD38nnfrInavO452WbkXCI37SVsdIu9VhvjEOojXBGY,31834
|
|
16
16
|
abstractcore/cli/vision_config.py,sha256=jJzO4zBexh8SqSKp6YKOXdMDSv4AL4Ztl5Xi-5c4KyY,17869
|
|
17
17
|
abstractcore/core/__init__.py,sha256=2h-86U4QkCQ4gzZ4iRusSTMlkODiUS6tKjZHiEXz6rM,684
|
|
18
18
|
abstractcore/core/enums.py,sha256=BhkVnHC-X1_377JDmqd-2mnem9GdBLqixWlYzlP_FJU,695
|
|
19
|
-
abstractcore/core/factory.py,sha256=
|
|
19
|
+
abstractcore/core/factory.py,sha256=ec7WGW2JKK-dhDplziTAeRkebEUFymtEEZ_bS5qkpqY,2798
|
|
20
20
|
abstractcore/core/interface.py,sha256=-VAY0nlsTnWN_WghiuMC7iE7xUdZfYOg6KlgrAPi14Y,14086
|
|
21
21
|
abstractcore/core/retry.py,sha256=wNlUAxfmvdO_uVWb4iqkhTqd7O1oRwXxqvVQaLXQOw0,14538
|
|
22
|
-
abstractcore/core/session.py,sha256=
|
|
23
|
-
abstractcore/core/types.py,sha256=
|
|
22
|
+
abstractcore/core/session.py,sha256=fdqhnufCWrV022RjQ-Xfb1KFv_s9-GzetSSR-QuXv-Q,36452
|
|
23
|
+
abstractcore/core/types.py,sha256=jj44i07kMjdg9FQ3mA_fK6r_M0Lcgt1RQpy1Ra5w-eI,4578
|
|
24
24
|
abstractcore/embeddings/__init__.py,sha256=hR3xZyqcRm4c2pq1dIa5lxj_-Bk70Zad802JQN4joWo,637
|
|
25
25
|
abstractcore/embeddings/manager.py,sha256=uFVbRPHx_R-kVMVA7N7_7EzeUmCJCeN9Dv0EV8Jko24,52245
|
|
26
26
|
abstractcore/embeddings/models.py,sha256=bsPAzL6gv57AVii8O15PT0kxfwRkOml3f3njJN4UDi4,4874
|
|
@@ -47,19 +47,18 @@ abstractcore/processing/__init__.py,sha256=t6hiakQjcZROT4pw9ZFt2q6fF3vf5VpdMKG2E
|
|
|
47
47
|
abstractcore/processing/basic_extractor.py,sha256=3x-3BdIHgLvqLnLF6K1-P4qVaLIpAnNIIutaJi7lDQM,49832
|
|
48
48
|
abstractcore/processing/basic_judge.py,sha256=tKWJrg_tY4vCHzWgXxz0ZjgLXBYYfpMcpG7vl03hJcM,32218
|
|
49
49
|
abstractcore/processing/basic_summarizer.py,sha256=XHNxMQ_8aLStTeUo6_2JaThlct12Htpz7ORmm0iuJsg,25495
|
|
50
|
-
abstractcore/providers/__init__.py,sha256=
|
|
51
|
-
abstractcore/providers/anthropic_provider.py,sha256=
|
|
50
|
+
abstractcore/providers/__init__.py,sha256=n-2RMNm3QpKxHw9EOjv8icRMRnfJp5Xg0uSVzHCW3BI,1222
|
|
51
|
+
abstractcore/providers/anthropic_provider.py,sha256=R87Z_DNNdeA4LMSxx84pqo8saKFz38dHCJMBpc-rL70,21552
|
|
52
52
|
abstractcore/providers/base.py,sha256=YfrqM3c7wLT19vspL7goUO6Bv-z1691ZkCM2wxvQX4s,51501
|
|
53
|
-
abstractcore/providers/huggingface_provider.py,sha256=
|
|
54
|
-
abstractcore/providers/lmstudio_provider.py,sha256=
|
|
55
|
-
abstractcore/providers/mlx_provider.py,sha256=
|
|
56
|
-
abstractcore/providers/
|
|
57
|
-
abstractcore/providers/
|
|
58
|
-
abstractcore/providers/
|
|
59
|
-
abstractcore/providers/registry.py,sha256=c0hxp9RRa-uipGotaAu48fHXc_HGlLcOxC1k763mzhU,16596
|
|
53
|
+
abstractcore/providers/huggingface_provider.py,sha256=pgpeSwpwyNB_5GDyLEz2OSTu9me-GAJzQ116dGtpCvQ,49012
|
|
54
|
+
abstractcore/providers/lmstudio_provider.py,sha256=odT6luVR3POVcq2ZqkINLyLoCAu_YGpLLj3fEddNliU,21021
|
|
55
|
+
abstractcore/providers/mlx_provider.py,sha256=sDgxf_kVJJwxprQWVef9w2CLOu2dLES8D0Vf5tY6PzE,18463
|
|
56
|
+
abstractcore/providers/ollama_provider.py,sha256=1bE80NCj_TQADxRCiu9luyLuI_gZe2EO5pCKoC4VhQM,21740
|
|
57
|
+
abstractcore/providers/openai_provider.py,sha256=1s7JJalyIBOvLB7UAUwXbTc2aYrYSWg7hJjKGnCX1qU,23313
|
|
58
|
+
abstractcore/providers/registry.py,sha256=fKFrN6Z3o5Gi0dfwvXDPtrrJXDjx9oPSfjWjZf-NJBc,15883
|
|
60
59
|
abstractcore/providers/streaming.py,sha256=VnffBV_CU9SAKzghL154OoFyEdDsiLwUNXPahyU41Bw,31342
|
|
61
60
|
abstractcore/server/__init__.py,sha256=1DSAz_YhQtnKv7sNi5TMQV8GFujctDOabgvAdilQE0o,249
|
|
62
|
-
abstractcore/server/app.py,sha256=
|
|
61
|
+
abstractcore/server/app.py,sha256=7pG5ZkZqYNnyby4jyvp3_NKl5nNDmZpOhv_-F8Jruy4,96580
|
|
63
62
|
abstractcore/structured/__init__.py,sha256=VXRQHGcm-iaYnLOBPin2kyhvhhQA0kaGt_pcNDGsE_8,339
|
|
64
63
|
abstractcore/structured/handler.py,sha256=Vb15smiR81JGDXX2RLkY2Exuj67J7a6C-xwVrZoXp0I,17134
|
|
65
64
|
abstractcore/structured/retry.py,sha256=BN_PvrWybyU1clMy2cult1-TVxFSMaVqiCPmmXvA5aI,3805
|
|
@@ -77,10 +76,10 @@ abstractcore/utils/message_preprocessor.py,sha256=GdHkm6tmrgjm3PwHRSCjIsq1XLkbhy
|
|
|
77
76
|
abstractcore/utils/self_fixes.py,sha256=QEDwNTW80iQM4ftfEY3Ghz69F018oKwLM9yeRCYZOvw,5886
|
|
78
77
|
abstractcore/utils/structured_logging.py,sha256=Vm-HviSa42G9DJCWmaEv4a0QG3NMsADD3ictLOs4En0,19952
|
|
79
78
|
abstractcore/utils/token_utils.py,sha256=eLwFmJ68p9WMFD_MHLMmeJRW6Oqx_4hKELB8FNQ2Mnk,21097
|
|
80
|
-
abstractcore/utils/version.py,sha256=
|
|
81
|
-
abstractcore-2.4.
|
|
82
|
-
abstractcore-2.4.
|
|
83
|
-
abstractcore-2.4.
|
|
84
|
-
abstractcore-2.4.
|
|
85
|
-
abstractcore-2.4.
|
|
86
|
-
abstractcore-2.4.
|
|
79
|
+
abstractcore/utils/version.py,sha256=4vhW55aGVOlb2i9Zm_ZxoGAPd5qtSm6ZN1WmH15tBYs,605
|
|
80
|
+
abstractcore-2.4.8.dist-info/licenses/LICENSE,sha256=PI2v_4HMvd6050uDD_4AY_8PzBnu2asa3RKbdDjowTA,1078
|
|
81
|
+
abstractcore-2.4.8.dist-info/METADATA,sha256=UfLD0Zi-Mco6B9NkoWuypgHZuUPJeGYyJVJjBBPlpsA,31907
|
|
82
|
+
abstractcore-2.4.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
83
|
+
abstractcore-2.4.8.dist-info/entry_points.txt,sha256=UdVmchBC_Lt3H4Vlkt5js-QDAkVlBbkCu1yCsswk-KE,454
|
|
84
|
+
abstractcore-2.4.8.dist-info/top_level.txt,sha256=DiNHBI35SIawW3N9Z-z0y6cQYNbXd32pvBkW0RLfScs,13
|
|
85
|
+
abstractcore-2.4.8.dist-info/RECORD,,
|
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Mock provider for testing purposes.
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
from typing import List, Dict, Any, Optional, Union, Iterator, Type
|
|
6
|
-
|
|
7
|
-
try:
|
|
8
|
-
from pydantic import BaseModel
|
|
9
|
-
PYDANTIC_AVAILABLE = True
|
|
10
|
-
except ImportError:
|
|
11
|
-
PYDANTIC_AVAILABLE = False
|
|
12
|
-
BaseModel = None
|
|
13
|
-
|
|
14
|
-
from .base import BaseProvider
|
|
15
|
-
from ..core.types import GenerateResponse
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class MockProvider(BaseProvider):
|
|
19
|
-
"""Simple mock provider for testing core functionality."""
|
|
20
|
-
|
|
21
|
-
def __init__(self, model: str = "mock-model", **kwargs):
|
|
22
|
-
super().__init__(model, **kwargs)
|
|
23
|
-
|
|
24
|
-
# Handle timeout parameter for mock provider
|
|
25
|
-
self._handle_timeout_parameter(kwargs)
|
|
26
|
-
|
|
27
|
-
# Mock provider uses prompted strategy for structured output
|
|
28
|
-
self.model_capabilities = {"structured_output": "prompted"}
|
|
29
|
-
|
|
30
|
-
def generate(self, *args, **kwargs):
|
|
31
|
-
"""Public generate method that includes telemetry"""
|
|
32
|
-
return self.generate_with_telemetry(*args, **kwargs)
|
|
33
|
-
|
|
34
|
-
def _generate_internal(self,
|
|
35
|
-
prompt: str,
|
|
36
|
-
messages: Optional[List[Dict[str, str]]] = None,
|
|
37
|
-
system_prompt: Optional[str] = None,
|
|
38
|
-
tools: Optional[List[Dict[str, Any]]] = None,
|
|
39
|
-
stream: bool = False,
|
|
40
|
-
response_model: Optional[Type[BaseModel]] = None,
|
|
41
|
-
**kwargs) -> Union[GenerateResponse, Iterator[GenerateResponse]]:
|
|
42
|
-
"""Mock generation implementation"""
|
|
43
|
-
|
|
44
|
-
if stream:
|
|
45
|
-
return self._stream_response(prompt)
|
|
46
|
-
else:
|
|
47
|
-
return self._single_response(prompt, response_model)
|
|
48
|
-
|
|
49
|
-
def _single_response(self, prompt: str, response_model: Optional[Type[BaseModel]] = None) -> GenerateResponse:
|
|
50
|
-
"""Generate single mock response"""
|
|
51
|
-
|
|
52
|
-
if response_model and PYDANTIC_AVAILABLE:
|
|
53
|
-
# Generate valid JSON for structured output
|
|
54
|
-
content = self._generate_mock_json(response_model)
|
|
55
|
-
else:
|
|
56
|
-
content = f"Mock response to: {prompt}"
|
|
57
|
-
|
|
58
|
-
return GenerateResponse(
|
|
59
|
-
content=content,
|
|
60
|
-
model=self.model,
|
|
61
|
-
finish_reason="stop",
|
|
62
|
-
usage=self._calculate_mock_usage(prompt, content)
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
def _calculate_mock_usage(self, prompt: str, response: str) -> Dict[str, int]:
|
|
66
|
-
"""Calculate mock token usage using centralized token utilities."""
|
|
67
|
-
from ..utils.token_utils import TokenUtils
|
|
68
|
-
|
|
69
|
-
prompt_tokens = TokenUtils.estimate_tokens(prompt, self.model)
|
|
70
|
-
completion_tokens = TokenUtils.estimate_tokens(response, self.model)
|
|
71
|
-
total_tokens = prompt_tokens + completion_tokens
|
|
72
|
-
|
|
73
|
-
return {
|
|
74
|
-
"prompt_tokens": prompt_tokens,
|
|
75
|
-
"completion_tokens": completion_tokens,
|
|
76
|
-
"total_tokens": total_tokens
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
def _stream_response(self, prompt: str) -> Iterator[GenerateResponse]:
|
|
80
|
-
"""Generate streaming mock responses"""
|
|
81
|
-
words = f"Mock response to: {prompt}".split()
|
|
82
|
-
for i, word in enumerate(words):
|
|
83
|
-
yield GenerateResponse(
|
|
84
|
-
content=word + (" " if i < len(words) - 1 else ""),
|
|
85
|
-
model=self.model,
|
|
86
|
-
finish_reason="stop" if i == len(words) - 1 else None
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
def _generate_mock_json(self, model_class: Type[BaseModel]) -> str:
|
|
90
|
-
"""Generate valid JSON for Pydantic model"""
|
|
91
|
-
import json
|
|
92
|
-
|
|
93
|
-
# Create mock data based on field types
|
|
94
|
-
mock_data = {}
|
|
95
|
-
for field_name, field_info in model_class.model_fields.items():
|
|
96
|
-
field_type = field_info.annotation
|
|
97
|
-
|
|
98
|
-
# Handle basic types
|
|
99
|
-
if field_type == str:
|
|
100
|
-
mock_data[field_name] = f"mock_{field_name}"
|
|
101
|
-
elif field_type == int:
|
|
102
|
-
mock_data[field_name] = 42
|
|
103
|
-
elif field_type == float:
|
|
104
|
-
mock_data[field_name] = 3.14
|
|
105
|
-
elif field_type == bool:
|
|
106
|
-
mock_data[field_name] = True
|
|
107
|
-
else:
|
|
108
|
-
# For complex types, provide reasonable defaults
|
|
109
|
-
mock_data[field_name] = f"mock_{field_name}"
|
|
110
|
-
|
|
111
|
-
return json.dumps(mock_data)
|
|
112
|
-
|
|
113
|
-
def _handle_timeout_parameter(self, kwargs: Dict[str, Any]) -> None:
|
|
114
|
-
"""
|
|
115
|
-
Handle timeout parameter for Mock provider.
|
|
116
|
-
|
|
117
|
-
Mock provider simulates responses instantly, so timeout parameters
|
|
118
|
-
don't apply. If a non-None timeout is provided, it's accepted but
|
|
119
|
-
has no effect on mock generation.
|
|
120
|
-
|
|
121
|
-
Args:
|
|
122
|
-
kwargs: Initialization kwargs that may contain timeout
|
|
123
|
-
"""
|
|
124
|
-
timeout_value = kwargs.get('timeout')
|
|
125
|
-
if timeout_value is not None:
|
|
126
|
-
# For mock provider, we accept timeout but it has no effect
|
|
127
|
-
# No warning needed since this is for testing
|
|
128
|
-
self._timeout = timeout_value
|
|
129
|
-
else:
|
|
130
|
-
# Keep None value
|
|
131
|
-
self._timeout = None
|
|
132
|
-
|
|
133
|
-
def _update_http_client_timeout(self) -> None:
|
|
134
|
-
"""
|
|
135
|
-
Mock provider doesn't use HTTP clients.
|
|
136
|
-
Timeout changes have no effect on mock responses.
|
|
137
|
-
"""
|
|
138
|
-
# No-op for mock provider - no HTTP clients used
|
|
139
|
-
pass
|
|
140
|
-
|
|
141
|
-
def get_capabilities(self) -> List[str]:
|
|
142
|
-
"""Get mock capabilities"""
|
|
143
|
-
return ["tools", "streaming", "vision"]
|
|
144
|
-
|
|
145
|
-
def validate_config(self) -> bool:
|
|
146
|
-
"""Validate mock provider configuration"""
|
|
147
|
-
return True
|
|
148
|
-
|
|
149
|
-
def list_available_models(self, **kwargs) -> List[str]:
|
|
150
|
-
"""List available mock models for testing."""
|
|
151
|
-
return [
|
|
152
|
-
"mock-model",
|
|
153
|
-
"mock-chat-model",
|
|
154
|
-
"mock-streaming-model",
|
|
155
|
-
"mock-structured-model",
|
|
156
|
-
"mock-vision-model"
|
|
157
|
-
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|