abstractcore 2.4.9__py3-none-any.whl → 2.5.0__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.
@@ -0,0 +1,10 @@
1
+ """
2
+ AbstractCore Configuration Module
3
+
4
+ Provides configuration management and command-line interface for AbstractCore.
5
+ """
6
+
7
+ from .vision_config import handle_vision_commands, add_vision_arguments
8
+ from .manager import get_config_manager
9
+
10
+ __all__ = ['handle_vision_commands', 'add_vision_arguments', 'get_config_manager']
@@ -264,6 +264,11 @@ def add_arguments(parser: argparse.ArgumentParser):
264
264
 
265
265
  def print_status():
266
266
  """Print comprehensive configuration status with improved readability."""
267
+ if not CONFIG_AVAILABLE or get_config_manager is None:
268
+ print("❌ Configuration system not available")
269
+ print("💡 The AbstractCore configuration module is missing")
270
+ return
271
+
267
272
  config_manager = get_config_manager()
268
273
  status = config_manager.get_status()
269
274
 
@@ -494,6 +499,12 @@ def interactive_configure():
494
499
 
495
500
  def handle_commands(args) -> bool:
496
501
  """Handle AbstractCore configuration commands."""
502
+ if not CONFIG_AVAILABLE or get_config_manager is None:
503
+ print("❌ Error: Configuration system not available")
504
+ print("💡 The AbstractCore configuration module is missing or not properly installed")
505
+ print("💡 Please reinstall AbstractCore or check your installation")
506
+ return True
507
+
497
508
  config_manager = get_config_manager()
498
509
  handled = False
499
510
 
@@ -0,0 +1,344 @@
1
+ """
2
+ AbstractCore Configuration Manager
3
+
4
+ Provides centralized configuration management for AbstractCore.
5
+ """
6
+
7
+ import json
8
+ import os
9
+ from pathlib import Path
10
+ from typing import Dict, Any, Optional, Tuple
11
+ from dataclasses import dataclass, asdict
12
+
13
+
14
+ @dataclass
15
+ class VisionConfig:
16
+ """Vision configuration settings."""
17
+ strategy: str = "disabled"
18
+ caption_provider: Optional[str] = None
19
+ caption_model: Optional[str] = None
20
+ fallback_chain: list = None
21
+ local_models_path: Optional[str] = None
22
+
23
+ def __post_init__(self):
24
+ if self.fallback_chain is None:
25
+ self.fallback_chain = []
26
+
27
+
28
+ @dataclass
29
+ class EmbeddingsConfig:
30
+ """Embeddings configuration settings."""
31
+ provider: Optional[str] = "huggingface"
32
+ model: Optional[str] = "all-minilm-l6-v2"
33
+
34
+
35
+ @dataclass
36
+ class AppDefaults:
37
+ """Per-application default configurations."""
38
+ cli_provider: Optional[str] = "huggingface"
39
+ cli_model: Optional[str] = "unsloth/Qwen3-4B-Instruct-2507-GGUF"
40
+ summarizer_provider: Optional[str] = "huggingface"
41
+ summarizer_model: Optional[str] = "unsloth/Qwen3-4B-Instruct-2507-GGUF"
42
+ extractor_provider: Optional[str] = "huggingface"
43
+ extractor_model: Optional[str] = "unsloth/Qwen3-4B-Instruct-2507-GGUF"
44
+ judge_provider: Optional[str] = "huggingface"
45
+ judge_model: Optional[str] = "unsloth/Qwen3-4B-Instruct-2507-GGUF"
46
+
47
+
48
+ @dataclass
49
+ class DefaultModels:
50
+ """Global default model configurations."""
51
+ global_provider: Optional[str] = None
52
+ global_model: Optional[str] = None
53
+ chat_model: Optional[str] = None
54
+ code_model: Optional[str] = None
55
+
56
+
57
+ @dataclass
58
+ class ApiKeysConfig:
59
+ """API keys configuration."""
60
+ openai: Optional[str] = None
61
+ anthropic: Optional[str] = None
62
+ google: Optional[str] = None
63
+
64
+
65
+ @dataclass
66
+ class CacheConfig:
67
+ """Cache configuration settings."""
68
+ default_cache_dir: str = "~/.cache/abstractcore"
69
+ huggingface_cache_dir: str = "~/.cache/huggingface"
70
+ local_models_cache_dir: str = "~/.abstractcore/models"
71
+
72
+
73
+ @dataclass
74
+ class LoggingConfig:
75
+ """Logging configuration settings."""
76
+ console_level: str = "WARNING"
77
+ file_level: str = "DEBUG"
78
+ file_logging_enabled: bool = False
79
+ log_base_dir: Optional[str] = None
80
+ verbatim_enabled: bool = True
81
+ console_json: bool = False
82
+ file_json: bool = True
83
+
84
+
85
+ @dataclass
86
+ class AbstractCoreConfig:
87
+ """Main configuration class."""
88
+ vision: VisionConfig
89
+ embeddings: EmbeddingsConfig
90
+ app_defaults: AppDefaults
91
+ default_models: DefaultModels
92
+ api_keys: ApiKeysConfig
93
+ cache: CacheConfig
94
+ logging: LoggingConfig
95
+
96
+ @classmethod
97
+ def default(cls):
98
+ """Create default configuration."""
99
+ return cls(
100
+ vision=VisionConfig(),
101
+ embeddings=EmbeddingsConfig(),
102
+ app_defaults=AppDefaults(),
103
+ default_models=DefaultModels(),
104
+ api_keys=ApiKeysConfig(),
105
+ cache=CacheConfig(),
106
+ logging=LoggingConfig()
107
+ )
108
+
109
+
110
+ class ConfigurationManager:
111
+ """Manages AbstractCore configuration."""
112
+
113
+ def __init__(self):
114
+ self.config_dir = Path.home() / ".abstractcore" / "config"
115
+ self.config_file = self.config_dir / "abstractcore.json"
116
+ self.config = self._load_config()
117
+
118
+ def _load_config(self) -> AbstractCoreConfig:
119
+ """Load configuration from file or create default."""
120
+ if self.config_file.exists():
121
+ try:
122
+ with open(self.config_file, 'r') as f:
123
+ data = json.load(f)
124
+ return self._dict_to_config(data)
125
+ except Exception:
126
+ # If loading fails, return default config
127
+ return AbstractCoreConfig.default()
128
+ else:
129
+ return AbstractCoreConfig.default()
130
+
131
+ def _dict_to_config(self, data: Dict[str, Any]) -> AbstractCoreConfig:
132
+ """Convert dictionary to config object."""
133
+ # Create config objects from dictionary data
134
+ vision = VisionConfig(**data.get('vision', {}))
135
+ embeddings = EmbeddingsConfig(**data.get('embeddings', {}))
136
+ app_defaults = AppDefaults(**data.get('app_defaults', {}))
137
+ default_models = DefaultModels(**data.get('default_models', {}))
138
+ api_keys = ApiKeysConfig(**data.get('api_keys', {}))
139
+ cache = CacheConfig(**data.get('cache', {}))
140
+ logging = LoggingConfig(**data.get('logging', {}))
141
+
142
+ return AbstractCoreConfig(
143
+ vision=vision,
144
+ embeddings=embeddings,
145
+ app_defaults=app_defaults,
146
+ default_models=default_models,
147
+ api_keys=api_keys,
148
+ cache=cache,
149
+ logging=logging
150
+ )
151
+
152
+ def _save_config(self):
153
+ """Save configuration to file."""
154
+ self.config_dir.mkdir(parents=True, exist_ok=True)
155
+
156
+ # Convert config to dictionary
157
+ config_dict = {
158
+ 'vision': asdict(self.config.vision),
159
+ 'embeddings': asdict(self.config.embeddings),
160
+ 'app_defaults': asdict(self.config.app_defaults),
161
+ 'default_models': asdict(self.config.default_models),
162
+ 'api_keys': asdict(self.config.api_keys),
163
+ 'cache': asdict(self.config.cache),
164
+ 'logging': asdict(self.config.logging)
165
+ }
166
+
167
+ with open(self.config_file, 'w') as f:
168
+ json.dump(config_dict, f, indent=2)
169
+
170
+ def set_vision_provider(self, provider: str, model: str) -> bool:
171
+ """Set vision provider and model."""
172
+ try:
173
+ self.config.vision.strategy = "two_stage"
174
+ self.config.vision.caption_provider = provider
175
+ self.config.vision.caption_model = model
176
+ self._save_config()
177
+ return True
178
+ except Exception:
179
+ return False
180
+
181
+ def set_vision_caption(self, model: str) -> bool:
182
+ """Set vision caption model (deprecated)."""
183
+ # Auto-detect provider from model name
184
+ provider = self._detect_provider_from_model(model)
185
+ if provider:
186
+ return self.set_vision_provider(provider, model)
187
+ return False
188
+
189
+ def _detect_provider_from_model(self, model: str) -> Optional[str]:
190
+ """Detect provider from model name."""
191
+ model_lower = model.lower()
192
+
193
+ if any(x in model_lower for x in ['qwen2.5vl', 'llama3.2-vision', 'llava']):
194
+ return "ollama"
195
+ elif any(x in model_lower for x in ['gpt-4', 'gpt-4o']):
196
+ return "openai"
197
+ elif any(x in model_lower for x in ['claude-3']):
198
+ return "anthropic"
199
+ elif '/' in model:
200
+ return "lmstudio"
201
+
202
+ return None
203
+
204
+ def get_status(self) -> Dict[str, Any]:
205
+ """Get configuration status."""
206
+ return {
207
+ "config_file": str(self.config_file),
208
+ "vision": {
209
+ "strategy": self.config.vision.strategy,
210
+ "status": "✅ Ready" if self.config.vision.caption_provider else "❌ Not configured",
211
+ "caption_provider": self.config.vision.caption_provider,
212
+ "caption_model": self.config.vision.caption_model
213
+ },
214
+ "app_defaults": {
215
+ "cli": {
216
+ "provider": self.config.app_defaults.cli_provider,
217
+ "model": self.config.app_defaults.cli_model
218
+ },
219
+ "summarizer": {
220
+ "provider": self.config.app_defaults.summarizer_provider,
221
+ "model": self.config.app_defaults.summarizer_model
222
+ },
223
+ "extractor": {
224
+ "provider": self.config.app_defaults.extractor_provider,
225
+ "model": self.config.app_defaults.extractor_model
226
+ },
227
+ "judge": {
228
+ "provider": self.config.app_defaults.judge_provider,
229
+ "model": self.config.app_defaults.judge_model
230
+ }
231
+ },
232
+ "global_defaults": {
233
+ "provider": self.config.default_models.global_provider,
234
+ "model": self.config.default_models.global_model,
235
+ "chat_model": self.config.default_models.chat_model,
236
+ "code_model": self.config.default_models.code_model
237
+ },
238
+ "embeddings": {
239
+ "status": "✅ Ready",
240
+ "provider": self.config.embeddings.provider,
241
+ "model": self.config.embeddings.model
242
+ },
243
+ "streaming": {
244
+ "cli_stream_default": False # Default value
245
+ },
246
+ "logging": {
247
+ "console_level": self.config.logging.console_level,
248
+ "file_level": self.config.logging.file_level,
249
+ "file_logging_enabled": self.config.logging.file_logging_enabled
250
+ },
251
+ "cache": {
252
+ "default_cache_dir": self.config.cache.default_cache_dir
253
+ },
254
+ "api_keys": {
255
+ "openai": "✅ Set" if self.config.api_keys.openai else "❌ Not set",
256
+ "anthropic": "✅ Set" if self.config.api_keys.anthropic else "❌ Not set",
257
+ "google": "✅ Set" if self.config.api_keys.google else "❌ Not set"
258
+ }
259
+ }
260
+
261
+ def set_global_default_model(self, provider_model: str) -> bool:
262
+ """Set global default model in provider/model format."""
263
+ try:
264
+ if '/' in provider_model:
265
+ provider, model = provider_model.split('/', 1)
266
+ else:
267
+ # Assume it's just a model name, use default provider
268
+ provider = "ollama"
269
+ model = provider_model
270
+
271
+ self.config.default_models.global_provider = provider
272
+ self.config.default_models.global_model = model
273
+ self._save_config()
274
+ return True
275
+ except Exception:
276
+ return False
277
+
278
+ def set_app_default(self, app_name: str, provider: str, model: str) -> bool:
279
+ """Set app-specific default provider and model."""
280
+ try:
281
+ if app_name == "cli":
282
+ self.config.app_defaults.cli_provider = provider
283
+ self.config.app_defaults.cli_model = model
284
+ elif app_name == "summarizer":
285
+ self.config.app_defaults.summarizer_provider = provider
286
+ self.config.app_defaults.summarizer_model = model
287
+ elif app_name == "extractor":
288
+ self.config.app_defaults.extractor_provider = provider
289
+ self.config.app_defaults.extractor_model = model
290
+ elif app_name == "judge":
291
+ self.config.app_defaults.judge_provider = provider
292
+ self.config.app_defaults.judge_model = model
293
+ else:
294
+ raise ValueError(f"Unknown app: {app_name}")
295
+
296
+ self._save_config()
297
+ return True
298
+ except Exception:
299
+ return False
300
+
301
+ def set_api_key(self, provider: str, key: str) -> bool:
302
+ """Set API key for a provider."""
303
+ try:
304
+ if provider == "openai":
305
+ self.config.api_keys.openai = key
306
+ elif provider == "anthropic":
307
+ self.config.api_keys.anthropic = key
308
+ elif provider == "google":
309
+ self.config.api_keys.google = key
310
+ else:
311
+ return False
312
+
313
+ self._save_config()
314
+ return True
315
+ except Exception:
316
+ return False
317
+
318
+ def get_app_default(self, app_name: str) -> Tuple[str, str]:
319
+ """Get default provider and model for an app."""
320
+ app_defaults = self.config.app_defaults
321
+
322
+ if app_name == "cli":
323
+ return app_defaults.cli_provider, app_defaults.cli_model
324
+ elif app_name == "summarizer":
325
+ return app_defaults.summarizer_provider, app_defaults.summarizer_model
326
+ elif app_name == "extractor":
327
+ return app_defaults.extractor_provider, app_defaults.extractor_model
328
+ elif app_name == "judge":
329
+ return app_defaults.judge_provider, app_defaults.judge_model
330
+ else:
331
+ # Return default fallback
332
+ return "huggingface", "unsloth/Qwen3-4B-Instruct-2507-GGUF"
333
+
334
+
335
+ # Global instance
336
+ _config_manager = None
337
+
338
+
339
+ def get_config_manager() -> ConfigurationManager:
340
+ """Get the global configuration manager instance."""
341
+ global _config_manager
342
+ if _config_manager is None:
343
+ _config_manager = ConfigurationManager()
344
+ return _config_manager
@@ -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.9"
14
+ __version__ = "2.5.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: abstractcore
3
- Version: 2.4.9
3
+ Version: 2.5.0
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>
@@ -551,6 +551,10 @@ abstractcore --set-console-log-level NONE # Disable console logging
551
551
  abstractcore --enable-file-logging # Save logs to files
552
552
  abstractcore --enable-debug-logging # Full debug mode
553
553
 
554
+ # Configure vision for image analysis with text-only models
555
+ abstractcore --set-vision-provider ollama qwen2.5vl:7b
556
+ abstractcore --set-vision-provider lmstudio qwen/qwen3-vl-4b
557
+
554
558
  # Set API keys as needed
555
559
  abstractcore --set-api-key openai sk-your-key-here
556
560
  abstractcore --set-api-key anthropic your-anthropic-key
@@ -11,9 +11,10 @@ abstractcore/architectures/enums.py,sha256=9vIv2vDBEKhxwzwH9iaSAyf-iVj3p8y9loMeN
11
11
  abstractcore/assets/architecture_formats.json,sha256=CIf6SaR_IJs1D7Uvd1K3zWngIXJ_yq3DM_IE3wnpCHY,16076
12
12
  abstractcore/assets/model_capabilities.json,sha256=iUkDiljyZUZzPlpYCOFgStXyc6e7dvOfReYQ0HFrX9Q,49703
13
13
  abstractcore/assets/session_schema.json,sha256=hMCVrq3KSyVExrMGzuykf7bU-z6WyIVuEGU8du9_zUY,10570
14
- abstractcore/cli/__init__.py,sha256=rUjLjZSK3wENSw4g_iN43Bc2i5cggcEmj4NPXBMohdc,241
15
- abstractcore/cli/main.py,sha256=cZBOY3Cj7a4SUxqp7p3FeuS-iUnCdYOf1-v4CICIni0,32073
16
- abstractcore/cli/vision_config.py,sha256=jJzO4zBexh8SqSKp6YKOXdMDSv4AL4Ztl5Xi-5c4KyY,17869
14
+ abstractcore/config/__init__.py,sha256=4mHX5z5Sq8R8xh78tb9jjZLaz_oBNW1eh914OsdDTxs,318
15
+ abstractcore/config/main.py,sha256=PaFzy0Z03mhu293_ato_8qjZJunz1AcXVYny56ct7Zw,32602
16
+ abstractcore/config/manager.py,sha256=6dYtVlwrXxcasDfz98CyocZ1nH9RPiD-yDKtsD3L7Ic,12508
17
+ abstractcore/config/vision_config.py,sha256=jJzO4zBexh8SqSKp6YKOXdMDSv4AL4Ztl5Xi-5c4KyY,17869
17
18
  abstractcore/core/__init__.py,sha256=2h-86U4QkCQ4gzZ4iRusSTMlkODiUS6tKjZHiEXz6rM,684
18
19
  abstractcore/core/enums.py,sha256=BhkVnHC-X1_377JDmqd-2mnem9GdBLqixWlYzlP_FJU,695
19
20
  abstractcore/core/factory.py,sha256=ec7WGW2JKK-dhDplziTAeRkebEUFymtEEZ_bS5qkpqY,2798
@@ -76,10 +77,10 @@ abstractcore/utils/message_preprocessor.py,sha256=GdHkm6tmrgjm3PwHRSCjIsq1XLkbhy
76
77
  abstractcore/utils/self_fixes.py,sha256=QEDwNTW80iQM4ftfEY3Ghz69F018oKwLM9yeRCYZOvw,5886
77
78
  abstractcore/utils/structured_logging.py,sha256=Vm-HviSa42G9DJCWmaEv4a0QG3NMsADD3ictLOs4En0,19952
78
79
  abstractcore/utils/token_utils.py,sha256=eLwFmJ68p9WMFD_MHLMmeJRW6Oqx_4hKELB8FNQ2Mnk,21097
79
- abstractcore/utils/version.py,sha256=no3N7qA8I5XGeoI1AfM5yAVEAnvR87_qtMTqqRVPDVc,605
80
- abstractcore-2.4.9.dist-info/licenses/LICENSE,sha256=PI2v_4HMvd6050uDD_4AY_8PzBnu2asa3RKbdDjowTA,1078
81
- abstractcore-2.4.9.dist-info/METADATA,sha256=rmaOtujlK0uq0n8YOFg1wht75czvj4beh9Wpv6obTVQ,31907
82
- abstractcore-2.4.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
83
- abstractcore-2.4.9.dist-info/entry_points.txt,sha256=UdVmchBC_Lt3H4Vlkt5js-QDAkVlBbkCu1yCsswk-KE,454
84
- abstractcore-2.4.9.dist-info/top_level.txt,sha256=DiNHBI35SIawW3N9Z-z0y6cQYNbXd32pvBkW0RLfScs,13
85
- abstractcore-2.4.9.dist-info/RECORD,,
80
+ abstractcore/utils/version.py,sha256=vI5E04BYQ3mKTIy4xhwGPse-tZLrIK0hLgJzTYZKJMQ,605
81
+ abstractcore-2.5.0.dist-info/licenses/LICENSE,sha256=PI2v_4HMvd6050uDD_4AY_8PzBnu2asa3RKbdDjowTA,1078
82
+ abstractcore-2.5.0.dist-info/METADATA,sha256=VVP8VO51qhQD_zfPbq25CQ3rbeeMD-UYyeloPU_lFbw,32084
83
+ abstractcore-2.5.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
84
+ abstractcore-2.5.0.dist-info/entry_points.txt,sha256=eUQvRc0sGl4pK3r2zhlGZro7Fk_wl4szw1hDE8SnlDI,460
85
+ abstractcore-2.5.0.dist-info/top_level.txt,sha256=DiNHBI35SIawW3N9Z-z0y6cQYNbXd32pvBkW0RLfScs,13
86
+ abstractcore-2.5.0.dist-info/RECORD,,
@@ -1,7 +1,7 @@
1
1
  [console_scripts]
2
- abstractcore = abstractcore.cli.main:main
2
+ abstractcore = abstractcore.config.main:main
3
3
  abstractcore-chat = abstractcore.utils.cli:main
4
- abstractcore-config = abstractcore.cli.main:main
4
+ abstractcore-config = abstractcore.config.main:main
5
5
  abstractcore-extractor = abstractcore.apps.extractor:main
6
6
  abstractcore-judge = abstractcore.apps.judge:main
7
7
  abstractcore-summarizer = abstractcore.apps.summarizer:main
@@ -1,9 +0,0 @@
1
- """
2
- AbstractCore CLI Module
3
-
4
- Provides command-line interface for AbstractCore configuration and tools.
5
- """
6
-
7
- from .vision_config import handle_vision_commands, add_vision_arguments
8
-
9
- __all__ = ['handle_vision_commands', 'add_vision_arguments']
File without changes