abstractcore 2.3.7__py3-none-any.whl → 2.3.9__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: abstractcore
3
- Version: 2.3.7
3
+ Version: 2.3.9
4
4
  Summary: Unified interface to all LLM providers with essential infrastructure for tool calling, streaming, and model management
5
5
  Author: Laurent-Philippe Albou
6
6
  Maintainer: Laurent-Philippe Albou
@@ -1,10 +1,10 @@
1
- abstractcore-2.3.7.dist-info/licenses/LICENSE,sha256=PI2v_4HMvd6050uDD_4AY_8PzBnu2asa3RKbdDjowTA,1078
1
+ abstractcore-2.3.9.dist-info/licenses/LICENSE,sha256=PI2v_4HMvd6050uDD_4AY_8PzBnu2asa3RKbdDjowTA,1078
2
2
  abstractllm/__init__.py,sha256=UWHXV-OyMpDeBsCFfLTTgVqkFTrj-uQWVqgFtkfjtxU,1859
3
3
  abstractllm/apps/__init__.py,sha256=H6fOR28gyBW8bDCEAft2RUZhNmRYA_7fF91szKBjhpA,30
4
4
  abstractllm/apps/__main__.py,sha256=YyAHo-0O4yAJ5JeinsioIM6Ytam2i4eq6UQt2oWB2s0,1579
5
- abstractllm/apps/extractor.py,sha256=T_9-GTMkTbswa2m3E294IO1eGiedNAWi0dK1xgdm0WE,23491
6
- abstractllm/apps/judge.py,sha256=gQBpwXLVLIyQxTwHblIPbNWRk6IGsRqkWq2dgj2vO1s,23278
7
- abstractllm/apps/summarizer.py,sha256=zf3MCJu5mwkOLhTxo41lHuc-462YkUYcFDB5pQu8Uv4,13786
5
+ abstractllm/apps/extractor.py,sha256=iBDBStD17K1YV22jMlAa2gAk8oywn96r6QW2DEMjZEs,23733
6
+ abstractllm/apps/judge.py,sha256=W6ILCLfDbMbuKsiaf0VOK86TEvi46z3yyViukG7kZWA,23151
7
+ abstractllm/apps/summarizer.py,sha256=YBTV98-YenxVYzZLVFB9YkGaCy57sHaEyzMFGPDvDyo,14128
8
8
  abstractllm/architectures/__init__.py,sha256=-4JucAM7JkMWShWKkePoclxrUHRKgaG36UTguJihE0U,1046
9
9
  abstractllm/architectures/detection.py,sha256=PYYzJ73tXI5ssJOpEL6-mHBmTreYXIq5f4nsfqtrpmw,9465
10
10
  abstractllm/architectures/enums.py,sha256=9vIv2vDBEKhxwzwH9iaSAyf-iVj3p8y9loMeN_mYTJ8,3821
@@ -23,16 +23,16 @@ abstractllm/embeddings/manager.py,sha256=YCW1r7ldd_mZshRjWNiNLIyhZktFl1SIiwgDDf4
23
23
  abstractllm/embeddings/models.py,sha256=bsPAzL6gv57AVii8O15PT0kxfwRkOml3f3njJN4UDi4,4874
24
24
  abstractllm/events/__init__.py,sha256=Z7WIDjerS3G6zjcjiRH3tHIbKdFNzlWnibHMNxkWWzo,11064
25
25
  abstractllm/processing/__init__.py,sha256=t6hiakQjcZROT4pw9ZFt2q6fF3vf5VpdMKG2EWlsFd8,540
26
- abstractllm/processing/basic_extractor.py,sha256=geB9_bqH9Z3hcKq7SheHtsqv0sgitolyMo-SPGgR2Cc,49673
27
- abstractllm/processing/basic_judge.py,sha256=pHfTSYRjNZq4N3BdpST-hpHgUu6PJFgkeuRo9bK-fiw,31879
28
- abstractllm/processing/basic_summarizer.py,sha256=P-tkTm0qMDLudQrctAAf1MYqZIa-hvDRZRg8isaVg1U,25334
26
+ abstractllm/processing/basic_extractor.py,sha256=IvigIkwF41G8SvsjVMoniHAu39gLKmKIp5-nTAoXx4E,49827
27
+ abstractllm/processing/basic_judge.py,sha256=LQkloGBL96w65E-Qtp-Gf78_KuOrrA72otzk18h-_WA,32213
28
+ abstractllm/processing/basic_summarizer.py,sha256=c420K0ExF3rrf-l4EuIAh2YKLxdxOloJujtuGTlk7AE,25488
29
29
  abstractllm/providers/__init__.py,sha256=UTpR2Bf_ICFG7M--1kxUmNXs4gl026Tp-KI9zJlvMKU,574
30
30
  abstractllm/providers/anthropic_provider.py,sha256=BM8Vu89c974yicvFwlsJ5C3N0wR9Kkt1pOszViWCwAQ,19694
31
- abstractllm/providers/base.py,sha256=rDxd5XglfZuZNj94iwjWc3ItPSddMQ92Y2G624mb60M,42780
32
- abstractllm/providers/huggingface_provider.py,sha256=4u3-Z4txonlKXE3DazvgUbxtzVfmdk-ZHsKPSaIfwD4,40785
33
- abstractllm/providers/lmstudio_provider.py,sha256=HcMltGyiXrLb2acA_XE4wDi1d-X2VZiT3eV1IjMF150,15701
34
- abstractllm/providers/mlx_provider.py,sha256=qusbTNvwWB8ydKVwv0adCLpMLFVT2RvX4LYHW-jUxlQ,14166
35
- abstractllm/providers/mock_provider.py,sha256=qORJxy_Yk0X970GiQG4JfU-T2IUx6C_PNASKM-M7ya8,4597
31
+ abstractllm/providers/base.py,sha256=Pp1CuJmhsH_4PZBrRKzmccvbhvgu6JS-hvDNJkpElfE,42820
32
+ abstractllm/providers/huggingface_provider.py,sha256=3RLqdZUbA9GgWRB0jpjtUMEs2FuzQwUlwC0mU-A1ysQ,42282
33
+ abstractllm/providers/lmstudio_provider.py,sha256=vhZJWDVu9hiC1wCoZoNODY14axXsy_dv52TQ13bsxmw,16004
34
+ abstractllm/providers/mlx_provider.py,sha256=eANGeexmJIS4KWn77fRBOJRkXvwgh7irAtu3kDVIVBA,15629
35
+ abstractllm/providers/mock_provider.py,sha256=tIjA57Hlwu3vNODOZShNn0tY9HWvz1p4z-HyD_bsvbo,5741
36
36
  abstractllm/providers/ollama_provider.py,sha256=SkXD5gjuzeu9Thqnt4pRPSi-cjWxwuZGV2x5YMm26jo,19340
37
37
  abstractllm/providers/openai_provider.py,sha256=xGFrSkbCrsBnWnho1U2aMCBdzfCqf121wU1EFGmU3YQ,21678
38
38
  abstractllm/providers/streaming.py,sha256=KQUH1DB-7UvuZP0_Wrcajj1P-9jdqnQfpIEeSYkxVFU,31153
@@ -54,9 +54,9 @@ abstractllm/utils/cli.py,sha256=PcA5AKvAr3bADT9x9OytfE4MDUVh48q8DcbIu9CLkj8,5687
54
54
  abstractllm/utils/self_fixes.py,sha256=QEDwNTW80iQM4ftfEY3Ghz69F018oKwLM9yeRCYZOvw,5886
55
55
  abstractllm/utils/structured_logging.py,sha256=2_bbMRjOvf0gHsRejncel_-PrhYUsOUySX_eaPcQopc,15827
56
56
  abstractllm/utils/token_utils.py,sha256=wAZpqiPh4eL8ppl1lNjqTzxCmlvdJ1Xu62UJyaDq09Y,21096
57
- abstractllm/utils/version.py,sha256=QyoMwQlCZK5rKvZLjbmcREXZU6Z8CC4a29TpUO4do04,1386
58
- abstractcore-2.3.7.dist-info/METADATA,sha256=lcnxke_jx-tKffNdv6UM5r45KRHN-YWt9Uos3Zu1gpc,19399
59
- abstractcore-2.3.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
60
- abstractcore-2.3.7.dist-info/entry_points.txt,sha256=plz04HNXbCbQkmEQj_8xKWW8x7zBxus68HD_04-IARM,306
61
- abstractcore-2.3.7.dist-info/top_level.txt,sha256=Md-8odCjB7hTNnE5hucnifAoLrL9HvRPffZmCq2jpoI,12
62
- abstractcore-2.3.7.dist-info/RECORD,,
57
+ abstractllm/utils/version.py,sha256=qbYRDP1iRl-eg3KmpLRoIZcJHiyGDC7qZgJJwcRgurQ,605
58
+ abstractcore-2.3.9.dist-info/METADATA,sha256=Hbqs0h67_AlDTRW676trCHF_I_6n0J8qjOauSIeM46U,19399
59
+ abstractcore-2.3.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
60
+ abstractcore-2.3.9.dist-info/entry_points.txt,sha256=plz04HNXbCbQkmEQj_8xKWW8x7zBxus68HD_04-IARM,306
61
+ abstractcore-2.3.9.dist-info/top_level.txt,sha256=Md-8odCjB7hTNnE5hucnifAoLrL9HvRPffZmCq2jpoI,12
62
+ abstractcore-2.3.9.dist-info/RECORD,,
@@ -52,6 +52,18 @@ from ..processing import BasicExtractor
52
52
  from ..core.factory import create_llm
53
53
 
54
54
 
55
+ def timeout_type(value):
56
+ """Parse timeout value - accepts None, 'none', or float"""
57
+ if value is None:
58
+ return None
59
+ if isinstance(value, str) and value.lower() == 'none':
60
+ return None
61
+ try:
62
+ return float(value)
63
+ except ValueError:
64
+ raise argparse.ArgumentTypeError(f"Invalid timeout value: {value}. Use 'none' for unlimited or a number in seconds.")
65
+
66
+
55
67
  def read_file_content(file_path: str) -> str:
56
68
  """
57
69
  Read content from various file types
@@ -295,9 +307,9 @@ Default model setup:
295
307
 
296
308
  parser.add_argument(
297
309
  '--timeout',
298
- type=float,
299
- default=600.0,
300
- help='HTTP request timeout in seconds for LLM providers (default: 300, i.e., 5 minutes). Increase for large models like 36B+ parameters'
310
+ type=timeout_type,
311
+ default=None,
312
+ help='HTTP request timeout in seconds for LLM providers (default: unlimited). Use "none" for unlimited timeout or specify seconds (e.g., 600 for 10 minutes)'
301
313
  )
302
314
 
303
315
  parser.add_argument(
@@ -342,13 +354,7 @@ Default model setup:
342
354
  sys.exit(1)
343
355
 
344
356
  # Validate timeout parameter
345
- if args.timeout < 30.0:
346
- print("Error: Timeout must be at least 30 seconds")
347
- sys.exit(1)
348
357
 
349
- if args.timeout > 7200.0: # 2 hours
350
- print("Error: Timeout cannot exceed 7200 seconds (2 hours)")
351
- sys.exit(1)
352
358
 
353
359
  # Validate provider/model pair
354
360
  if args.provider and not args.model:
@@ -423,7 +429,8 @@ Default model setup:
423
429
  llm=llm,
424
430
  max_chunk_size=adjusted_chunk_size,
425
431
  max_tokens=args.max_tokens,
426
- max_output_tokens=args.max_output_tokens
432
+ max_output_tokens=args.max_output_tokens,
433
+ timeout=args.timeout
427
434
  )
428
435
  else:
429
436
  # Default configuration
@@ -434,7 +441,8 @@ Default model setup:
434
441
  extractor = BasicExtractor(
435
442
  max_chunk_size=args.chunk_size,
436
443
  max_tokens=args.max_tokens,
437
- max_output_tokens=args.max_output_tokens
444
+ max_output_tokens=args.max_output_tokens,
445
+ timeout=args.timeout
438
446
  )
439
447
  except RuntimeError as e:
440
448
  # Handle default model not available
abstractllm/apps/judge.py CHANGED
@@ -383,9 +383,8 @@ Default model setup:
383
383
 
384
384
  parser.add_argument(
385
385
  '--timeout',
386
- type=float,
387
- default=300.0,
388
- help='HTTP request timeout in seconds for LLM providers (default: 300)'
386
+ default=None,
387
+ help='HTTP request timeout in seconds for LLM providers (default: None = infininity)'
389
388
  )
390
389
 
391
390
  # Parse arguments
@@ -398,9 +397,6 @@ Default model setup:
398
397
  sys.exit(1)
399
398
 
400
399
  # Validate timeout
401
- if args.timeout < 30.0:
402
- print("Error: Timeout must be at least 30 seconds")
403
- sys.exit(1)
404
400
 
405
401
  # Validate provider/model pair
406
402
  if args.provider and not args.model:
@@ -240,6 +240,12 @@ Default model setup:
240
240
  help='Show detailed progress information'
241
241
  )
242
242
 
243
+ parser.add_argument(
244
+ '--timeout',
245
+ default=None,
246
+ help='HTTP request timeout in seconds for LLM providers (default: None = unlimited)'
247
+ )
248
+
243
249
  # Parse arguments
244
250
  args = parser.parse_args()
245
251
 
@@ -286,12 +292,13 @@ Default model setup:
286
292
  if args.verbose:
287
293
  print(f"Initializing summarizer ({args.provider}, {args.model}, {args.max_tokens} token context, {args.max_output_tokens} output tokens)...")
288
294
 
289
- llm = create_llm(args.provider, model=args.model, max_tokens=args.max_tokens, max_output_tokens=args.max_output_tokens)
295
+ llm = create_llm(args.provider, model=args.model, max_tokens=args.max_tokens, max_output_tokens=args.max_output_tokens, timeout=args.timeout)
290
296
  summarizer = BasicSummarizer(
291
297
  llm,
292
298
  max_chunk_size=args.chunk_size,
293
299
  max_tokens=args.max_tokens,
294
- max_output_tokens=args.max_output_tokens
300
+ max_output_tokens=args.max_output_tokens,
301
+ timeout=args.timeout
295
302
  )
296
303
  else:
297
304
  # Default configuration with chunk size override
@@ -302,12 +309,13 @@ Default model setup:
302
309
  print(f"Initializing summarizer (ollama, gemma3:1b-it-qat, {args.max_tokens} token context, {args.max_output_tokens} output tokens, {args.chunk_size} chunk size)...")
303
310
 
304
311
  try:
305
- llm = create_llm("ollama", model="gemma3:1b-it-qat", max_tokens=args.max_tokens, max_output_tokens=args.max_output_tokens)
312
+ llm = create_llm("ollama", model="gemma3:1b-it-qat", max_tokens=args.max_tokens, max_output_tokens=args.max_output_tokens, timeout=args.timeout)
306
313
  summarizer = BasicSummarizer(
307
314
  llm,
308
315
  max_chunk_size=args.chunk_size,
309
316
  max_tokens=args.max_tokens,
310
- max_output_tokens=args.max_output_tokens
317
+ max_output_tokens=args.max_output_tokens,
318
+ timeout=args.timeout
311
319
  )
312
320
  except Exception as e:
313
321
  # Handle default model not available
@@ -327,7 +335,8 @@ Default model setup:
327
335
  summarizer = BasicSummarizer(
328
336
  max_chunk_size=args.chunk_size,
329
337
  max_tokens=args.max_tokens,
330
- max_output_tokens=args.max_output_tokens
338
+ max_output_tokens=args.max_output_tokens,
339
+ timeout=args.timeout
331
340
  )
332
341
  except RuntimeError as e:
333
342
  # Handle default model not available
@@ -53,7 +53,8 @@ class BasicExtractor:
53
53
  llm: Optional[AbstractLLMInterface] = None,
54
54
  max_chunk_size: int = 8000,
55
55
  max_tokens: int = 32000,
56
- max_output_tokens: int = 8000
56
+ max_output_tokens: int = 8000,
57
+ timeout: Optional[float] = None
57
58
  ):
58
59
  """Initialize the extractor
59
60
 
@@ -62,10 +63,11 @@ class BasicExtractor:
62
63
  max_chunk_size: Maximum characters per chunk for long documents (default 8000)
63
64
  max_tokens: Maximum total tokens for LLM context (default 32000)
64
65
  max_output_tokens: Maximum tokens for LLM output generation (default 8000)
66
+ timeout: HTTP request timeout in seconds. None for unlimited timeout (default None)
65
67
  """
66
68
  if llm is None:
67
69
  try:
68
- self.llm = create_llm("ollama", model="qwen3:4b-instruct-2507-q4_K_M", max_tokens=max_tokens, max_output_tokens=max_output_tokens)
70
+ self.llm = create_llm("ollama", model="qwen3:4b-instruct-2507-q4_K_M", max_tokens=max_tokens, max_output_tokens=max_output_tokens, timeout=timeout)
69
71
  except Exception as e:
70
72
  error_msg = (
71
73
  f"❌ Failed to initialize default Ollama model 'qwen3:4b-instruct-2507-q4_K_M': {e}\n\n"
@@ -118,7 +118,8 @@ class BasicJudge:
118
118
  temperature: float = 0.1, # Low temperature for consistent evaluation
119
119
  max_tokens: int = 32000,
120
120
  max_output_tokens: int = 8000,
121
- debug: bool = False
121
+ debug: bool = False,
122
+ timeout: Optional[float] = None
122
123
  ):
123
124
  """Initialize the judge
124
125
 
@@ -128,12 +129,13 @@ class BasicJudge:
128
129
  max_tokens: Maximum total tokens for LLM context (default 32000)
129
130
  max_output_tokens: Maximum tokens for LLM output generation (default 8000)
130
131
  debug: Enable debug output showing raw LLM responses (default False)
132
+ timeout: HTTP request timeout in seconds. None for unlimited timeout (default None)
131
133
  """
132
134
  if llm is None:
133
135
  try:
134
136
  # Use low temperature for consistent evaluation
135
137
  self.llm = create_llm("ollama", model="qwen3:4b-instruct-2507-q4_K_M",
136
- max_tokens=max_tokens, max_output_tokens=max_output_tokens, temperature=temperature)
138
+ max_tokens=max_tokens, max_output_tokens=max_output_tokens, temperature=temperature, timeout=timeout)
137
139
  except Exception as e:
138
140
  error_msg = (
139
141
  f"❌ Failed to initialize default Ollama model 'qwen3:4b-instruct-2507-q4_K_M': {e}\n\n"
@@ -663,6 +665,7 @@ def create_judge(
663
665
  max_tokens: int = 32000,
664
666
  max_output_tokens: int = 8000,
665
667
  debug: bool = False,
668
+ timeout: Optional[float] = None,
666
669
  **kwargs
667
670
  ) -> BasicJudge:
668
671
  """
@@ -675,13 +678,14 @@ def create_judge(
675
678
  max_tokens: Maximum total tokens for LLM context (default 32000)
676
679
  max_output_tokens: Maximum tokens for LLM output generation (default 8000)
677
680
  debug: Enable debug output showing raw LLM responses (default False)
681
+ timeout: HTTP request timeout in seconds. None for unlimited timeout (default None)
678
682
  **kwargs: Additional arguments passed to create_llm
679
683
 
680
684
  Returns:
681
685
  BasicJudge instance
682
686
  """
683
687
  if provider and model:
684
- llm = create_llm(provider, model=model, temperature=temperature, max_tokens=max_tokens, max_output_tokens=max_output_tokens, **kwargs)
685
- return BasicJudge(llm=llm, temperature=temperature, max_tokens=max_tokens, max_output_tokens=max_output_tokens, debug=debug)
688
+ llm = create_llm(provider, model=model, temperature=temperature, max_tokens=max_tokens, max_output_tokens=max_output_tokens, timeout=timeout, **kwargs)
689
+ return BasicJudge(llm=llm, temperature=temperature, max_tokens=max_tokens, max_output_tokens=max_output_tokens, debug=debug, timeout=timeout)
686
690
  else:
687
- return BasicJudge(temperature=temperature, max_tokens=max_tokens, max_output_tokens=max_output_tokens, debug=debug)
691
+ return BasicJudge(temperature=temperature, max_tokens=max_tokens, max_output_tokens=max_output_tokens, debug=debug, timeout=timeout)
@@ -82,7 +82,8 @@ class BasicSummarizer:
82
82
  llm: Optional[AbstractLLMInterface] = None,
83
83
  max_chunk_size: int = 8000,
84
84
  max_tokens: int = 32000,
85
- max_output_tokens: int = 8000
85
+ max_output_tokens: int = 8000,
86
+ timeout: Optional[float] = None
86
87
  ):
87
88
  """
88
89
  Initialize the summarizer
@@ -91,12 +92,13 @@ class BasicSummarizer:
91
92
  llm: AbstractLLM instance (any provider). If None, attempts to create ollama gemma3:1b-it-qat
92
93
  max_chunk_size: Maximum characters per chunk for long documents (default 8000)
93
94
  max_tokens: Maximum total tokens for LLM context (default 32000)
94
- max_output_tokens: Maximum tokens for LLM output generation (default 4000)
95
+ max_output_tokens: Maximum tokens for LLM output generation (default 8000)
96
+ timeout: HTTP request timeout in seconds. None for unlimited timeout (default None)
95
97
  """
96
98
  if llm is None:
97
99
  try:
98
100
  # Default to gemma3:1b-it-qat with configurable token limits
99
- self.llm = create_llm("ollama", model="gemma3:1b-it-qat", max_tokens=max_tokens, max_output_tokens=max_output_tokens)
101
+ self.llm = create_llm("ollama", model="gemma3:1b-it-qat", max_tokens=max_tokens, max_output_tokens=max_output_tokens, timeout=timeout)
100
102
  except Exception as e:
101
103
  error_msg = (
102
104
  f"❌ Failed to initialize default Ollama model 'gemma3:1b-it-qat': {e}\n\n"
@@ -48,8 +48,9 @@ class BaseProvider(AbstractLLMInterface, ABC):
48
48
  self.model_capabilities = get_model_capabilities(model)
49
49
 
50
50
  # Setup timeout configuration
51
- self._timeout = kwargs.get('timeout', 300.0) # Default 300 seconds (5 minutes) for HTTP requests
52
- self._tool_timeout = kwargs.get('tool_timeout', 300.0) # Default 300 seconds for tool execution
51
+ # Default to None for unlimited timeout
52
+ self._timeout = kwargs.get('timeout', None) # Default None for unlimited HTTP requests
53
+ self._tool_timeout = kwargs.get('tool_timeout', None) # Default None for unlimited tool execution
53
54
 
54
55
  # Setup tool execution mode
55
56
  # execute_tools: True = AbstractCore executes tools (legacy mode)
@@ -62,6 +62,9 @@ class HuggingFaceProvider(BaseProvider):
62
62
 
63
63
  super().__init__(model, **kwargs)
64
64
 
65
+ # Handle timeout parameter for local models
66
+ self._handle_timeout_parameter(kwargs)
67
+
65
68
  # Initialize tool handler
66
69
  self.tool_handler = UniversalToolHandler(model)
67
70
 
@@ -427,6 +430,40 @@ class HuggingFaceProvider(BaseProvider):
427
430
 
428
431
  return sorted(similar_models)
429
432
 
433
+ def _handle_timeout_parameter(self, kwargs: Dict[str, Any]) -> None:
434
+ """
435
+ Handle timeout parameter for HuggingFace provider.
436
+
437
+ Since HuggingFace models run locally (both transformers and GGUF),
438
+ timeout parameters don't apply. If a non-None timeout is provided,
439
+ issue a warning and treat it as None (infinity).
440
+
441
+ Args:
442
+ kwargs: Initialization kwargs that may contain timeout
443
+ """
444
+ timeout_value = kwargs.get('timeout')
445
+ if timeout_value is not None:
446
+ import warnings
447
+ warnings.warn(
448
+ f"HuggingFace provider runs models locally and does not support timeout parameters. "
449
+ f"Provided timeout={timeout_value} will be ignored and treated as None (unlimited).",
450
+ UserWarning,
451
+ stacklevel=3
452
+ )
453
+ # Force timeout to None for local models
454
+ self._timeout = None
455
+ else:
456
+ # Keep None value (unlimited timeout is appropriate for local models)
457
+ self._timeout = None
458
+
459
+ def _update_http_client_timeout(self) -> None:
460
+ """
461
+ HuggingFace provider doesn't use HTTP clients for model inference.
462
+ Local models (transformers and GGUF) don't have timeout constraints.
463
+ """
464
+ # No-op for local models - they don't use HTTP clients
465
+ pass
466
+
430
467
  def generate(self, *args, **kwargs):
431
468
  """Public generate method that includes telemetry"""
432
469
  return self.generate_with_telemetry(*args, **kwargs)
@@ -30,10 +30,11 @@ class LMStudioProvider(BaseProvider):
30
30
 
31
31
  self.base_url = base_url.rstrip('/')
32
32
 
33
- # Ensure timeout is properly set and not None
33
+ # Get timeout value - None means unlimited timeout
34
34
  timeout_value = getattr(self, '_timeout', None)
35
- if timeout_value is None:
36
- timeout_value = 300.0
35
+ # Validate timeout if provided (None is allowed for unlimited)
36
+ if timeout_value is not None and timeout_value <= 0:
37
+ timeout_value = None # Invalid timeout becomes unlimited
37
38
 
38
39
  try:
39
40
  self.client = httpx.Client(timeout=timeout_value)
@@ -295,10 +296,11 @@ class LMStudioProvider(BaseProvider):
295
296
  # Create new client with updated timeout
296
297
  self.client.close()
297
298
 
298
- # Ensure timeout is not None
299
+ # Get timeout value - None means unlimited timeout
299
300
  timeout_value = getattr(self, '_timeout', None)
300
- if timeout_value is None:
301
- timeout_value = 300.0
301
+ # Validate timeout if provided (None is allowed for unlimited)
302
+ if timeout_value is not None and timeout_value <= 0:
303
+ timeout_value = None # Invalid timeout becomes unlimited
302
304
 
303
305
  self.client = httpx.Client(timeout=timeout_value)
304
306
  except Exception as e:
@@ -24,6 +24,9 @@ class MLXProvider(BaseProvider):
24
24
  def __init__(self, model: str = "mlx-community/Mistral-7B-Instruct-v0.1-4bit", **kwargs):
25
25
  super().__init__(model, **kwargs)
26
26
 
27
+ # Handle timeout parameter for local models
28
+ self._handle_timeout_parameter(kwargs)
29
+
27
30
  # Initialize tool handler
28
31
  self.tool_handler = UniversalToolHandler(model)
29
32
 
@@ -93,6 +96,40 @@ class MLXProvider(BaseProvider):
93
96
  if hasattr(self, 'logger'):
94
97
  self.logger.warning(f"Error during unload: {e}")
95
98
 
99
+ def _handle_timeout_parameter(self, kwargs: Dict[str, Any]) -> None:
100
+ """
101
+ Handle timeout parameter for MLX provider.
102
+
103
+ Since MLX models run locally on Apple Silicon,
104
+ timeout parameters don't apply. If a non-None timeout is provided,
105
+ issue a warning and treat it as None (infinity).
106
+
107
+ Args:
108
+ kwargs: Initialization kwargs that may contain timeout
109
+ """
110
+ timeout_value = kwargs.get('timeout')
111
+ if timeout_value is not None:
112
+ import warnings
113
+ warnings.warn(
114
+ f"MLX provider runs models locally on Apple Silicon and does not support timeout parameters. "
115
+ f"Provided timeout={timeout_value} will be ignored and treated as None (unlimited).",
116
+ UserWarning,
117
+ stacklevel=3
118
+ )
119
+ # Force timeout to None for local models
120
+ self._timeout = None
121
+ else:
122
+ # Keep None value (unlimited timeout is appropriate for local models)
123
+ self._timeout = None
124
+
125
+ def _update_http_client_timeout(self) -> None:
126
+ """
127
+ MLX provider doesn't use HTTP clients for model inference.
128
+ Local models on Apple Silicon don't have timeout constraints.
129
+ """
130
+ # No-op for local models - they don't use HTTP clients
131
+ pass
132
+
96
133
  def generate(self, *args, **kwargs):
97
134
  """Public generate method that includes telemetry"""
98
135
  return self.generate_with_telemetry(*args, **kwargs)
@@ -20,6 +20,10 @@ class MockProvider(BaseProvider):
20
20
 
21
21
  def __init__(self, model: str = "mock-model", **kwargs):
22
22
  super().__init__(model, **kwargs)
23
+
24
+ # Handle timeout parameter for mock provider
25
+ self._handle_timeout_parameter(kwargs)
26
+
23
27
  # Mock provider uses prompted strategy for structured output
24
28
  self.model_capabilities = {"structured_output": "prompted"}
25
29
 
@@ -106,6 +110,34 @@ class MockProvider(BaseProvider):
106
110
 
107
111
  return json.dumps(mock_data)
108
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
+
109
141
  def get_capabilities(self) -> List[str]:
110
142
  """Get mock capabilities"""
111
143
  return ["tools", "streaming", "vision"]
@@ -1,44 +1,14 @@
1
1
  """
2
2
  Version management for AbstractCore.
3
3
 
4
- This module provides a single source of truth for the package version,
5
- reading it dynamically from pyproject.toml to avoid version synchronization issues.
6
- """
7
-
8
- import os
9
- import tomllib
10
- from pathlib import Path
11
-
12
-
13
- def get_version() -> str:
14
- """
15
- Get the package version from pyproject.toml.
16
-
17
- Returns:
18
- str: The package version
19
-
20
- Raises:
21
- FileNotFoundError: If pyproject.toml is not found
22
- KeyError: If version is not found in pyproject.toml
23
- """
24
- # Get the project root directory (where pyproject.toml is located)
25
- # When imported as a module, we need to go up from abstractllm/utils/ to the project root
26
- current_dir = Path(__file__).parent # abstractllm/utils/
27
- project_root = current_dir.parent.parent # Go up two levels: utils -> abstractllm -> project_root
28
- pyproject_path = project_root / "pyproject.toml"
29
-
30
- if not pyproject_path.exists():
31
- raise FileNotFoundError(f"pyproject.toml not found at {pyproject_path}")
32
-
33
- # Read and parse pyproject.toml
34
- with open(pyproject_path, "rb") as f:
35
- data = tomllib.load(f)
36
-
37
- try:
38
- return data["project"]["version"]
39
- except KeyError:
40
- raise KeyError("Version not found in pyproject.toml [project] section")
4
+ This module provides the package version as a static constant that serves as the
5
+ single source of truth for the Python code. The version is also maintained in
6
+ pyproject.toml for packaging, requiring manual synchronization during releases.
41
7
 
8
+ This approach ensures reliable version access in all deployment scenarios,
9
+ including when the package is installed from PyPI where pyproject.toml is not available.
10
+ """
42
11
 
43
- # Set the version as a module-level constant
44
- __version__ = get_version()
12
+ # Package version - update this when releasing new versions
13
+ # This must be manually synchronized with the version in pyproject.toml
14
+ __version__ = "2.3.9"