abstractcore 2.4.6__py3-none-any.whl → 2.4.7__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/session.py +1 -1
- abstractcore/core/types.py +25 -1
- 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/mock_provider.py +17 -7
- abstractcore/providers/ollama_provider.py +10 -3
- abstractcore/providers/openai_provider.py +12 -3
- abstractcore/utils/version.py +1 -1
- {abstractcore-2.4.6.dist-info → abstractcore-2.4.7.dist-info}/METADATA +50 -4
- {abstractcore-2.4.6.dist-info → abstractcore-2.4.7.dist-info}/RECORD +18 -18
- {abstractcore-2.4.6.dist-info → abstractcore-2.4.7.dist-info}/WHEEL +0 -0
- {abstractcore-2.4.6.dist-info → abstractcore-2.4.7.dist-info}/entry_points.txt +0 -0
- {abstractcore-2.4.6.dist-info → abstractcore-2.4.7.dist-info}/licenses/LICENSE +0 -0
- {abstractcore-2.4.6.dist-info → abstractcore-2.4.7.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/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')
|
|
@@ -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]:
|
|
@@ -48,6 +48,12 @@ class MockProvider(BaseProvider):
|
|
|
48
48
|
|
|
49
49
|
def _single_response(self, prompt: str, response_model: Optional[Type[BaseModel]] = None) -> GenerateResponse:
|
|
50
50
|
"""Generate single mock response"""
|
|
51
|
+
import time
|
|
52
|
+
|
|
53
|
+
# Simulate generation time (10-100ms for mock)
|
|
54
|
+
start_time = time.time()
|
|
55
|
+
time.sleep(0.01 + (len(prompt) % 10) * 0.01) # 10-100ms based on prompt length
|
|
56
|
+
gen_time = round((time.time() - start_time) * 1000, 1)
|
|
51
57
|
|
|
52
58
|
if response_model and PYDANTIC_AVAILABLE:
|
|
53
59
|
# Generate valid JSON for structured output
|
|
@@ -59,21 +65,25 @@ class MockProvider(BaseProvider):
|
|
|
59
65
|
content=content,
|
|
60
66
|
model=self.model,
|
|
61
67
|
finish_reason="stop",
|
|
62
|
-
usage=self._calculate_mock_usage(prompt, content)
|
|
68
|
+
usage=self._calculate_mock_usage(prompt, content),
|
|
69
|
+
gen_time=gen_time
|
|
63
70
|
)
|
|
64
71
|
|
|
65
72
|
def _calculate_mock_usage(self, prompt: str, response: str) -> Dict[str, int]:
|
|
66
73
|
"""Calculate mock token usage using centralized token utilities."""
|
|
67
74
|
from ..utils.token_utils import TokenUtils
|
|
68
75
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
total_tokens =
|
|
76
|
+
input_tokens = TokenUtils.estimate_tokens(prompt, self.model)
|
|
77
|
+
output_tokens = TokenUtils.estimate_tokens(response, self.model)
|
|
78
|
+
total_tokens = input_tokens + output_tokens
|
|
72
79
|
|
|
73
80
|
return {
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
"total_tokens": total_tokens
|
|
81
|
+
"input_tokens": input_tokens,
|
|
82
|
+
"output_tokens": output_tokens,
|
|
83
|
+
"total_tokens": total_tokens,
|
|
84
|
+
# Keep legacy keys for backward compatibility
|
|
85
|
+
"prompt_tokens": input_tokens,
|
|
86
|
+
"completion_tokens": output_tokens
|
|
77
87
|
}
|
|
78
88
|
|
|
79
89
|
def _stream_response(self, prompt: str) -> 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
|
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.7"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: abstractcore
|
|
3
|
-
Version: 2.4.
|
|
3
|
+
Version: 2.4.7
|
|
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, Mock (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,7 +10,7 @@ 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
|
|
@@ -19,8 +19,8 @@ abstractcore/core/enums.py,sha256=BhkVnHC-X1_377JDmqd-2mnem9GdBLqixWlYzlP_FJU,69
|
|
|
19
19
|
abstractcore/core/factory.py,sha256=UdrNwQAvifvFS3LMjF5KO87m-2n1bJBryTs9pvesYcI,2804
|
|
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
|
|
@@ -48,14 +48,14 @@ abstractcore/processing/basic_extractor.py,sha256=3x-3BdIHgLvqLnLF6K1-P4qVaLIpAn
|
|
|
48
48
|
abstractcore/processing/basic_judge.py,sha256=tKWJrg_tY4vCHzWgXxz0ZjgLXBYYfpMcpG7vl03hJcM,32218
|
|
49
49
|
abstractcore/processing/basic_summarizer.py,sha256=XHNxMQ_8aLStTeUo6_2JaThlct12Htpz7ORmm0iuJsg,25495
|
|
50
50
|
abstractcore/providers/__init__.py,sha256=t8Kp4flH5GvZEC6dx-iYJSPeSxMODa2spXb8MqtlPy4,1282
|
|
51
|
-
abstractcore/providers/anthropic_provider.py,sha256=
|
|
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/mock_provider.py,sha256=
|
|
57
|
-
abstractcore/providers/ollama_provider.py,sha256=
|
|
58
|
-
abstractcore/providers/openai_provider.py,sha256=
|
|
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/mock_provider.py,sha256=x-frlBLxlqx6jlMoPnZN4aNv1pHacRYW_jlp0peI9FA,6168
|
|
57
|
+
abstractcore/providers/ollama_provider.py,sha256=1bE80NCj_TQADxRCiu9luyLuI_gZe2EO5pCKoC4VhQM,21740
|
|
58
|
+
abstractcore/providers/openai_provider.py,sha256=1s7JJalyIBOvLB7UAUwXbTc2aYrYSWg7hJjKGnCX1qU,23313
|
|
59
59
|
abstractcore/providers/registry.py,sha256=c0hxp9RRa-uipGotaAu48fHXc_HGlLcOxC1k763mzhU,16596
|
|
60
60
|
abstractcore/providers/streaming.py,sha256=VnffBV_CU9SAKzghL154OoFyEdDsiLwUNXPahyU41Bw,31342
|
|
61
61
|
abstractcore/server/__init__.py,sha256=1DSAz_YhQtnKv7sNi5TMQV8GFujctDOabgvAdilQE0o,249
|
|
@@ -77,10 +77,10 @@ abstractcore/utils/message_preprocessor.py,sha256=GdHkm6tmrgjm3PwHRSCjIsq1XLkbhy
|
|
|
77
77
|
abstractcore/utils/self_fixes.py,sha256=QEDwNTW80iQM4ftfEY3Ghz69F018oKwLM9yeRCYZOvw,5886
|
|
78
78
|
abstractcore/utils/structured_logging.py,sha256=Vm-HviSa42G9DJCWmaEv4a0QG3NMsADD3ictLOs4En0,19952
|
|
79
79
|
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.
|
|
80
|
+
abstractcore/utils/version.py,sha256=ktMUN5ql9UjgEbvyoT7ilNbOyvAiVtrcRN8hl3eQSvc,605
|
|
81
|
+
abstractcore-2.4.7.dist-info/licenses/LICENSE,sha256=PI2v_4HMvd6050uDD_4AY_8PzBnu2asa3RKbdDjowTA,1078
|
|
82
|
+
abstractcore-2.4.7.dist-info/METADATA,sha256=NZiFyhk87Pq3Pe-L7E0xo37RawH6YlFhhBiYp26Ocmc,31913
|
|
83
|
+
abstractcore-2.4.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
84
|
+
abstractcore-2.4.7.dist-info/entry_points.txt,sha256=UdVmchBC_Lt3H4Vlkt5js-QDAkVlBbkCu1yCsswk-KE,454
|
|
85
|
+
abstractcore-2.4.7.dist-info/top_level.txt,sha256=DiNHBI35SIawW3N9Z-z0y6cQYNbXd32pvBkW0RLfScs,13
|
|
86
|
+
abstractcore-2.4.7.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|