abstractcore 2.4.9__py3-none-any.whl → 2.5.2__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/apps/__main__.py +8 -1
- abstractcore/apps/deepsearch.py +644 -0
- abstractcore/apps/intent.py +614 -0
- abstractcore/assets/model_capabilities.json +50 -34
- abstractcore/config/__init__.py +10 -0
- abstractcore/{cli → config}/main.py +13 -1
- abstractcore/config/manager.py +355 -0
- abstractcore/core/session.py +46 -1
- abstractcore/processing/__init__.py +5 -1
- abstractcore/processing/basic_deepsearch.py +2173 -0
- abstractcore/processing/basic_intent.py +690 -0
- abstractcore/providers/anthropic_provider.py +1 -0
- abstractcore/providers/base.py +1 -0
- abstractcore/providers/huggingface_provider.py +95 -4
- abstractcore/providers/lmstudio_provider.py +14 -0
- abstractcore/providers/mlx_provider.py +76 -2
- abstractcore/providers/ollama_provider.py +6 -2
- abstractcore/providers/openai_provider.py +1 -0
- abstractcore/providers/registry.py +6 -6
- abstractcore/structured/handler.py +161 -1
- abstractcore/tools/common_tools.py +98 -3
- abstractcore/utils/cli.py +114 -1
- abstractcore/utils/version.py +1 -1
- {abstractcore-2.4.9.dist-info → abstractcore-2.5.2.dist-info}/METADATA +38 -18
- {abstractcore-2.4.9.dist-info → abstractcore-2.5.2.dist-info}/RECORD +30 -25
- {abstractcore-2.4.9.dist-info → abstractcore-2.5.2.dist-info}/entry_points.txt +6 -2
- abstractcore/cli/__init__.py +0 -9
- /abstractcore/{cli → config}/vision_config.py +0 -0
- {abstractcore-2.4.9.dist-info → abstractcore-2.5.2.dist-info}/WHEEL +0 -0
- {abstractcore-2.4.9.dist-info → abstractcore-2.5.2.dist-info}/licenses/LICENSE +0 -0
- {abstractcore-2.4.9.dist-info → abstractcore-2.5.2.dist-info}/top_level.txt +0 -0
|
@@ -1277,17 +1277,23 @@ def _parse_content_by_type(content_bytes: bytes, content_type: str, url: str, ex
|
|
|
1277
1277
|
# Final fallback with error replacement
|
|
1278
1278
|
text_content = content_bytes.decode('utf-8', errors='replace')
|
|
1279
1279
|
|
|
1280
|
-
# Parse based on content type
|
|
1280
|
+
# Parse based on content type with fallback content detection
|
|
1281
1281
|
if main_type.startswith('text/html') or main_type.startswith('application/xhtml'):
|
|
1282
1282
|
return _parse_html_content(text_content, url, extract_links)
|
|
1283
1283
|
|
|
1284
1284
|
elif main_type == 'application/json':
|
|
1285
1285
|
return _parse_json_content(text_content)
|
|
1286
1286
|
|
|
1287
|
-
elif main_type in ['application/xml', 'text/xml', 'application/rss+xml', 'application/atom+xml']:
|
|
1287
|
+
elif main_type in ['application/xml', 'text/xml', 'application/rss+xml', 'application/atom+xml', 'application/soap+xml']:
|
|
1288
1288
|
return _parse_xml_content(text_content)
|
|
1289
1289
|
|
|
1290
1290
|
elif main_type.startswith('text/'):
|
|
1291
|
+
# For generic text types, check if it's actually XML or JSON
|
|
1292
|
+
if text_content and text_content.strip():
|
|
1293
|
+
if _is_xml_content(text_content):
|
|
1294
|
+
return _parse_xml_content(text_content)
|
|
1295
|
+
elif _is_json_content(text_content):
|
|
1296
|
+
return _parse_json_content(text_content)
|
|
1291
1297
|
return _parse_text_content(text_content, main_type)
|
|
1292
1298
|
|
|
1293
1299
|
elif main_type.startswith('image/'):
|
|
@@ -1305,18 +1311,107 @@ def _parse_content_by_type(content_bytes: bytes, content_type: str, url: str, ex
|
|
|
1305
1311
|
f"Content size: {len(content_bytes):,} bytes"
|
|
1306
1312
|
|
|
1307
1313
|
|
|
1314
|
+
def _is_xml_content(content: str) -> bool:
|
|
1315
|
+
"""Detect if content is XML rather than HTML."""
|
|
1316
|
+
if not content:
|
|
1317
|
+
return False
|
|
1318
|
+
|
|
1319
|
+
content_lower = content.lower().strip()
|
|
1320
|
+
|
|
1321
|
+
# Check for XML declaration
|
|
1322
|
+
if content_lower.startswith('<?xml'):
|
|
1323
|
+
return True
|
|
1324
|
+
|
|
1325
|
+
# Check for common XML root elements without HTML indicators
|
|
1326
|
+
xml_indicators = ['<rss', '<feed', '<urlset', '<sitemap', '<soap:', '<xml']
|
|
1327
|
+
html_indicators = ['<!doctype html', '<html', '<head>', '<body>', '<div', '<span', '<p>', '<a ']
|
|
1328
|
+
|
|
1329
|
+
# Look at the first 1000 characters for indicators
|
|
1330
|
+
sample = content_lower[:1000]
|
|
1331
|
+
|
|
1332
|
+
# If we find HTML indicators, it's likely HTML
|
|
1333
|
+
if any(indicator in sample for indicator in html_indicators):
|
|
1334
|
+
return False
|
|
1335
|
+
|
|
1336
|
+
# If we find XML indicators without HTML indicators, it's likely XML
|
|
1337
|
+
if any(indicator in sample for indicator in xml_indicators):
|
|
1338
|
+
return True
|
|
1339
|
+
|
|
1340
|
+
# Check if it starts with a root element that looks like XML
|
|
1341
|
+
import re
|
|
1342
|
+
root_match = re.search(r'<([^?\s/>]+)', content)
|
|
1343
|
+
if root_match:
|
|
1344
|
+
root_element = root_match.group(1).lower()
|
|
1345
|
+
# Common XML root elements that are not HTML
|
|
1346
|
+
xml_roots = ['rss', 'feed', 'urlset', 'sitemap', 'configuration', 'data', 'response']
|
|
1347
|
+
if root_element in xml_roots:
|
|
1348
|
+
return True
|
|
1349
|
+
|
|
1350
|
+
return False
|
|
1351
|
+
|
|
1352
|
+
|
|
1353
|
+
def _is_json_content(content: str) -> bool:
|
|
1354
|
+
"""Detect if content is JSON."""
|
|
1355
|
+
if not content:
|
|
1356
|
+
return False
|
|
1357
|
+
|
|
1358
|
+
content_stripped = content.strip()
|
|
1359
|
+
|
|
1360
|
+
# Quick check for JSON structure
|
|
1361
|
+
if (content_stripped.startswith('{') and content_stripped.endswith('}')) or \
|
|
1362
|
+
(content_stripped.startswith('[') and content_stripped.endswith(']')):
|
|
1363
|
+
try:
|
|
1364
|
+
import json
|
|
1365
|
+
json.loads(content_stripped)
|
|
1366
|
+
return True
|
|
1367
|
+
except (json.JSONDecodeError, ValueError):
|
|
1368
|
+
pass
|
|
1369
|
+
|
|
1370
|
+
return False
|
|
1371
|
+
|
|
1372
|
+
|
|
1373
|
+
def _get_appropriate_parser(content: str) -> str:
|
|
1374
|
+
"""Get the appropriate BeautifulSoup parser for the content."""
|
|
1375
|
+
if not BS4_AVAILABLE:
|
|
1376
|
+
return None
|
|
1377
|
+
|
|
1378
|
+
# If lxml is available and content looks like XML, use xml parser
|
|
1379
|
+
if 'lxml' in BS4_PARSER and _is_xml_content(content):
|
|
1380
|
+
try:
|
|
1381
|
+
import lxml
|
|
1382
|
+
return 'xml'
|
|
1383
|
+
except ImportError:
|
|
1384
|
+
pass
|
|
1385
|
+
|
|
1386
|
+
# Default to the configured parser (lxml or html.parser)
|
|
1387
|
+
return BS4_PARSER
|
|
1388
|
+
|
|
1389
|
+
|
|
1308
1390
|
def _parse_html_content(html_content: str, url: str, extract_links: bool = True) -> str:
|
|
1309
1391
|
"""Parse HTML content and extract meaningful information."""
|
|
1310
1392
|
if not html_content:
|
|
1311
1393
|
return "❌ No HTML content to parse"
|
|
1312
1394
|
|
|
1395
|
+
# Detect if content is actually XML (fallback detection)
|
|
1396
|
+
if _is_xml_content(html_content):
|
|
1397
|
+
return _parse_xml_content(html_content)
|
|
1398
|
+
|
|
1313
1399
|
result_parts = []
|
|
1314
1400
|
result_parts.append("🌐 HTML Document Analysis")
|
|
1315
1401
|
|
|
1316
1402
|
# Use BeautifulSoup if available for better parsing
|
|
1317
1403
|
if BS4_AVAILABLE:
|
|
1318
1404
|
try:
|
|
1319
|
-
|
|
1405
|
+
# Choose appropriate parser based on content analysis
|
|
1406
|
+
parser = _get_appropriate_parser(html_content)
|
|
1407
|
+
|
|
1408
|
+
# Suppress XML parsing warnings when using HTML parser on XML content
|
|
1409
|
+
import warnings
|
|
1410
|
+
from bs4 import XMLParsedAsHTMLWarning
|
|
1411
|
+
|
|
1412
|
+
with warnings.catch_warnings():
|
|
1413
|
+
warnings.filterwarnings("ignore", category=XMLParsedAsHTMLWarning)
|
|
1414
|
+
soup = BeautifulSoup(html_content, parser)
|
|
1320
1415
|
|
|
1321
1416
|
# Extract title
|
|
1322
1417
|
title = soup.find('title')
|
abstractcore/utils/cli.py
CHANGED
|
@@ -39,7 +39,7 @@ except ImportError:
|
|
|
39
39
|
|
|
40
40
|
from .. import create_llm, BasicSession
|
|
41
41
|
from ..tools.common_tools import list_files, read_file, write_file, execute_command, search_files
|
|
42
|
-
from ..processing import BasicExtractor, BasicJudge
|
|
42
|
+
from ..processing import BasicExtractor, BasicJudge, BasicIntentAnalyzer
|
|
43
43
|
|
|
44
44
|
|
|
45
45
|
class SimpleCLI:
|
|
@@ -193,6 +193,10 @@ class SimpleCLI:
|
|
|
193
193
|
print(" • /facts - Display in chat")
|
|
194
194
|
print(" • /facts data - Save as data.jsonld")
|
|
195
195
|
print(" /judge Evaluate conversation quality")
|
|
196
|
+
print(" /intent [participant] Analyze intents behind conversation")
|
|
197
|
+
print(" • /intent - Analyze all participants")
|
|
198
|
+
print(" • /intent user - Focus on user intents")
|
|
199
|
+
print(" • /intent assistant - Focus on assistant intents")
|
|
196
200
|
|
|
197
201
|
print("\n⚙️ CONFIGURATION")
|
|
198
202
|
print("─" * 50)
|
|
@@ -317,6 +321,17 @@ class SimpleCLI:
|
|
|
317
321
|
elif cmd == 'judge':
|
|
318
322
|
self.handle_judge()
|
|
319
323
|
|
|
324
|
+
elif cmd.startswith('intent'):
|
|
325
|
+
# Parse /intent [participant] command
|
|
326
|
+
parts = cmd.split()
|
|
327
|
+
if len(parts) == 1:
|
|
328
|
+
# No participant specified - analyze all
|
|
329
|
+
self.handle_intent(None)
|
|
330
|
+
else:
|
|
331
|
+
# Participant specified
|
|
332
|
+
participant = parts[1]
|
|
333
|
+
self.handle_intent(participant)
|
|
334
|
+
|
|
320
335
|
elif cmd.startswith('system'):
|
|
321
336
|
# Parse /system [prompt] command
|
|
322
337
|
if cmd == 'system':
|
|
@@ -623,6 +638,103 @@ class SimpleCLI:
|
|
|
623
638
|
import traceback
|
|
624
639
|
traceback.print_exc()
|
|
625
640
|
|
|
641
|
+
def handle_intent(self, focus_participant: str = None):
|
|
642
|
+
"""Handle /intent [participant] command - analyze intents behind conversation"""
|
|
643
|
+
messages = self.session.get_messages()
|
|
644
|
+
|
|
645
|
+
if len(messages) <= 1: # Only system message
|
|
646
|
+
print("📝 No conversation history to analyze intents from")
|
|
647
|
+
return
|
|
648
|
+
|
|
649
|
+
try:
|
|
650
|
+
if focus_participant:
|
|
651
|
+
print(f"🎯 Analyzing {focus_participant} intents in conversation...")
|
|
652
|
+
else:
|
|
653
|
+
print("🎯 Analyzing conversation intents for all participants...")
|
|
654
|
+
|
|
655
|
+
# Create intent analyzer using current provider for consistency
|
|
656
|
+
analyzer = BasicIntentAnalyzer(self.provider)
|
|
657
|
+
|
|
658
|
+
# Convert session messages to the format expected by intent analyzer
|
|
659
|
+
conversation_messages = [msg for msg in messages if msg.role != 'system']
|
|
660
|
+
message_dicts = [{"role": msg.role, "content": msg.content} for msg in conversation_messages]
|
|
661
|
+
|
|
662
|
+
if not message_dicts:
|
|
663
|
+
print("📝 No substantive conversation content found")
|
|
664
|
+
return
|
|
665
|
+
|
|
666
|
+
print(f" Processing {len(message_dicts)} messages...")
|
|
667
|
+
|
|
668
|
+
start_time = time.time()
|
|
669
|
+
|
|
670
|
+
# Analyze conversation intents
|
|
671
|
+
from ..processing.basic_intent import IntentDepth
|
|
672
|
+
results = analyzer.analyze_conversation_intents(
|
|
673
|
+
messages=message_dicts,
|
|
674
|
+
focus_participant=focus_participant,
|
|
675
|
+
depth=IntentDepth.UNDERLYING
|
|
676
|
+
)
|
|
677
|
+
|
|
678
|
+
duration = time.time() - start_time
|
|
679
|
+
print(f"✅ Intent analysis completed in {duration:.1f}s")
|
|
680
|
+
|
|
681
|
+
if not results:
|
|
682
|
+
print("❌ No intents could be analyzed from the conversation")
|
|
683
|
+
return
|
|
684
|
+
|
|
685
|
+
# Display results in a conversational format
|
|
686
|
+
print("\n🎯 CONVERSATION INTENT ANALYSIS")
|
|
687
|
+
print("=" * 60)
|
|
688
|
+
|
|
689
|
+
for participant, analysis in results.items():
|
|
690
|
+
print(f"\n👤 {participant.upper()} INTENTS:")
|
|
691
|
+
print("─" * 40)
|
|
692
|
+
|
|
693
|
+
# Primary Intent
|
|
694
|
+
primary = analysis.primary_intent
|
|
695
|
+
print(f"🎯 Primary Intent: {primary.intent_type.value.replace('_', ' ').title()}")
|
|
696
|
+
print(f" Description: {primary.description}")
|
|
697
|
+
print(f" Underlying Goal: {primary.underlying_goal}")
|
|
698
|
+
print(f" Emotional Undertone: {primary.emotional_undertone}")
|
|
699
|
+
print(f" Confidence: {primary.confidence:.2f} | Urgency: {primary.urgency_level:.2f}")
|
|
700
|
+
|
|
701
|
+
# Secondary Intents (show top 2 for brevity)
|
|
702
|
+
if analysis.secondary_intents:
|
|
703
|
+
print(f"\n🔄 Secondary Intents:")
|
|
704
|
+
for i, intent in enumerate(analysis.secondary_intents[:2], 1):
|
|
705
|
+
print(f" {i}. {intent.intent_type.value.replace('_', ' ').title()}")
|
|
706
|
+
print(f" Goal: {intent.underlying_goal}")
|
|
707
|
+
print(f" Confidence: {intent.confidence:.2f}")
|
|
708
|
+
|
|
709
|
+
# Key contextual factors (show top 3)
|
|
710
|
+
if analysis.contextual_factors:
|
|
711
|
+
print(f"\n🌍 Key Context Factors:")
|
|
712
|
+
for factor in analysis.contextual_factors[:3]:
|
|
713
|
+
print(f" • {factor}")
|
|
714
|
+
|
|
715
|
+
# Response approach
|
|
716
|
+
print(f"\n💡 Suggested Response Approach:")
|
|
717
|
+
# Truncate long response approaches for readability
|
|
718
|
+
response_approach = analysis.suggested_response_approach
|
|
719
|
+
if len(response_approach) > 200:
|
|
720
|
+
response_approach = response_approach[:197] + "..."
|
|
721
|
+
print(f" {response_approach}")
|
|
722
|
+
|
|
723
|
+
# Analysis metadata
|
|
724
|
+
print(f"\n📊 Analysis: {analysis.word_count_analyzed} words | "
|
|
725
|
+
f"Complexity: {analysis.intent_complexity:.2f} | "
|
|
726
|
+
f"Confidence: {analysis.overall_confidence:.2f} | "
|
|
727
|
+
f"Time: {duration:.1f}s")
|
|
728
|
+
|
|
729
|
+
print("\n" + "=" * 60)
|
|
730
|
+
print("💡 Note: This analysis identifies underlying motivations and goals behind communication")
|
|
731
|
+
|
|
732
|
+
except Exception as e:
|
|
733
|
+
print(f"❌ Intent analysis failed: {e}")
|
|
734
|
+
if self.debug_mode:
|
|
735
|
+
import traceback
|
|
736
|
+
traceback.print_exc()
|
|
737
|
+
|
|
626
738
|
def _format_conversation_for_extraction(self, messages):
|
|
627
739
|
"""Format conversation messages for fact extraction"""
|
|
628
740
|
formatted_lines = []
|
|
@@ -1337,6 +1449,7 @@ Key Commands:
|
|
|
1337
1449
|
/compact [focus] Compress chat history with optional focus
|
|
1338
1450
|
/facts [file] Extract knowledge facts
|
|
1339
1451
|
/judge Evaluate conversation quality
|
|
1452
|
+
/intent [participant] Analyze conversation intents and motivations
|
|
1340
1453
|
/system [prompt] View/change system prompt
|
|
1341
1454
|
|
|
1342
1455
|
Tools: list_files, search_files, read_file, write_file, execute_command
|
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.
|
|
14
|
+
__version__ = "2.5.2"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: abstractcore
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.5.2
|
|
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>
|
|
@@ -40,9 +40,11 @@ Provides-Extra: huggingface
|
|
|
40
40
|
Requires-Dist: transformers<5.0.0,>=4.30.0; extra == "huggingface"
|
|
41
41
|
Requires-Dist: torch<3.0.0,>=1.12.0; extra == "huggingface"
|
|
42
42
|
Requires-Dist: llama-cpp-python<1.0.0,>=0.2.0; extra == "huggingface"
|
|
43
|
+
Requires-Dist: outlines>=0.1.0; extra == "huggingface"
|
|
43
44
|
Provides-Extra: mlx
|
|
44
45
|
Requires-Dist: mlx<1.0.0,>=0.15.0; extra == "mlx"
|
|
45
46
|
Requires-Dist: mlx-lm<1.0.0,>=0.15.0; extra == "mlx"
|
|
47
|
+
Requires-Dist: outlines>=0.1.0; extra == "mlx"
|
|
46
48
|
Provides-Extra: embeddings
|
|
47
49
|
Requires-Dist: sentence-transformers<4.0.0,>=2.7.0; extra == "embeddings"
|
|
48
50
|
Requires-Dist: numpy<2.0.0,>=1.20.0; extra == "embeddings"
|
|
@@ -258,7 +260,7 @@ loaded_session = BasicSession.load('conversation.json', provider=llm)
|
|
|
258
260
|
|
|
259
261
|
### Media Handling
|
|
260
262
|
|
|
261
|
-
AbstractCore provides
|
|
263
|
+
AbstractCore provides unified media handling across all providers with automatic resolution optimization. Upload images, PDFs, and documents using the same simple API regardless of your provider.
|
|
262
264
|
|
|
263
265
|
```python
|
|
264
266
|
from abstractcore import create_llm
|
|
@@ -296,7 +298,7 @@ response = llm.generate(
|
|
|
296
298
|
- **Smart Resolution**: Automatically uses maximum resolution supported by each model
|
|
297
299
|
- **Format Support**: PNG, JPEG, GIF, WEBP, BMP, TIFF images; PDF, TXT, MD, CSV, TSV, JSON documents
|
|
298
300
|
- **Office Documents**: DOCX, XLSX, PPT (with `pip install abstractcore[all]`)
|
|
299
|
-
- **Vision Optimization**: Model-specific image processing for
|
|
301
|
+
- **Vision Optimization**: Model-specific image processing for vision results
|
|
300
302
|
|
|
301
303
|
**Provider compatibility:**
|
|
302
304
|
- **High-resolution vision**: GPT-4o (up to 4096x4096), Claude 3.5 Sonnet (up to 1568x1568)
|
|
@@ -310,11 +312,11 @@ response = llm.generate(
|
|
|
310
312
|
- **Provider Agnostic**: Seamlessly switch between OpenAI, Anthropic, Ollama, LMStudio, MLX, HuggingFace
|
|
311
313
|
- **Centralized Configuration**: Global defaults and app-specific preferences at `~/.abstractcore/config/abstractcore.json`
|
|
312
314
|
- **Intelligent Media Handling**: Upload images, PDFs, and documents with automatic maximum resolution optimization
|
|
313
|
-
- **Vision Model Support**: Smart image processing at each model's maximum capability
|
|
314
|
-
- **Document Processing**:
|
|
315
|
+
- **Vision Model Support**: Smart image processing at each model's maximum capability
|
|
316
|
+
- **Document Processing**: PDF extraction (PyMuPDF4LLM), Office documents (DOCX/XLSX/PPT), CSV/TSV analysis
|
|
315
317
|
- **Unified Tools**: Consistent tool calling across all providers
|
|
316
318
|
- **Session Management**: Persistent conversations with metadata, analytics, and complete serialization
|
|
317
|
-
- **Structured
|
|
319
|
+
- **Native Structured Output**: Server-side schema enforcement for Ollama and LMStudio (OpenAI and Anthropic also supported)
|
|
318
320
|
- **Streaming Support**: Real-time token generation for interactive experiences
|
|
319
321
|
- **Consistent Token Terminology**: Unified `input_tokens`, `output_tokens`, `total_tokens` across all providers
|
|
320
322
|
- **Embeddings**: Built-in support for semantic search and RAG applications
|
|
@@ -324,12 +326,12 @@ response = llm.generate(
|
|
|
324
326
|
|
|
325
327
|
| Provider | Status | SEED Support | Setup |
|
|
326
328
|
|----------|--------|-------------|-------|
|
|
327
|
-
| **OpenAI** | Full |
|
|
328
|
-
| **Anthropic** | Full |
|
|
329
|
-
| **Ollama** | Full |
|
|
330
|
-
| **LMStudio** | Full |
|
|
331
|
-
| **MLX** | Full |
|
|
332
|
-
| **HuggingFace** | Full |
|
|
329
|
+
| **OpenAI** | Full | Native | [Get API key](docs/prerequisites.md#openai-setup) |
|
|
330
|
+
| **Anthropic** | Full | Warning* | [Get API key](docs/prerequisites.md#anthropic-setup) |
|
|
331
|
+
| **Ollama** | Full | Native | [Install guide](docs/prerequisites.md#ollama-setup) |
|
|
332
|
+
| **LMStudio** | Full | Native | [Install guide](docs/prerequisites.md#lmstudio-setup) |
|
|
333
|
+
| **MLX** | Full | Native | [Setup guide](docs/prerequisites.md#mlx-setup) |
|
|
334
|
+
| **HuggingFace** | Full | Native | [Setup guide](docs/prerequisites.md#huggingface-setup) |
|
|
333
335
|
|
|
334
336
|
*Anthropic doesn't support seed parameters but issues a warning when provided. Use `temperature=0.0` for more consistent outputs.
|
|
335
337
|
|
|
@@ -374,7 +376,7 @@ response = client.chat.completions.create(
|
|
|
374
376
|
- Building web applications that need HTTP API
|
|
375
377
|
- Multi-language access (not just Python)
|
|
376
378
|
|
|
377
|
-
##
|
|
379
|
+
## AbstractCore CLI (Optional Interactive Testing Tool)
|
|
378
380
|
|
|
379
381
|
AbstractCore includes a **built-in CLI** for interactive testing, development, and conversation management. This is an internal testing tool, distinct from external agentic CLIs.
|
|
380
382
|
|
|
@@ -394,6 +396,7 @@ python -m abstractcore.utils.cli --provider anthropic --model claude-3-5-haiku-l
|
|
|
394
396
|
- Chat history compaction and management
|
|
395
397
|
- Fact extraction from conversations
|
|
396
398
|
- Conversation quality evaluation (LLM-as-a-judge)
|
|
399
|
+
- Intent analysis and deception detection
|
|
397
400
|
- Tool call testing and debugging
|
|
398
401
|
- System prompt management
|
|
399
402
|
- Multiple provider support
|
|
@@ -402,12 +405,13 @@ python -m abstractcore.utils.cli --provider anthropic --model claude-3-5-haiku-l
|
|
|
402
405
|
- `/compact` - Compress chat history while preserving context
|
|
403
406
|
- `/facts [file]` - Extract structured facts from conversation
|
|
404
407
|
- `/judge` - Evaluate conversation quality with feedback
|
|
408
|
+
- `/intent [participant]` - Analyze psychological intents and detect deception
|
|
405
409
|
- `/history [n]` - View conversation history
|
|
406
410
|
- `/stream` - Toggle real-time streaming
|
|
407
411
|
- `/system [prompt]` - Show or change system prompt
|
|
408
412
|
- `/status` - Show current provider, model, and capabilities
|
|
409
413
|
|
|
410
|
-
**Full Documentation:** [
|
|
414
|
+
**Full Documentation:** [AbstractCore CLI Guide](docs/acore-cli.md)
|
|
411
415
|
|
|
412
416
|
**When to use the CLI:**
|
|
413
417
|
- Interactive development and testing
|
|
@@ -418,7 +422,7 @@ python -m abstractcore.utils.cli --provider anthropic --model claude-3-5-haiku-l
|
|
|
418
422
|
|
|
419
423
|
## Built-in Applications (Ready-to-Use CLI Tools)
|
|
420
424
|
|
|
421
|
-
AbstractCore includes **
|
|
425
|
+
AbstractCore includes **four specialized command-line applications** for common LLM tasks. These are production-ready tools that can be used directly from the terminal without any Python programming.
|
|
422
426
|
|
|
423
427
|
### Available Applications
|
|
424
428
|
|
|
@@ -427,6 +431,7 @@ AbstractCore includes **three specialized command-line applications** for common
|
|
|
427
431
|
| **Summarizer** | Document summarization | `summarizer` |
|
|
428
432
|
| **Extractor** | Entity and relationship extraction | `extractor` |
|
|
429
433
|
| **Judge** | Text evaluation and scoring | `judge` |
|
|
434
|
+
| **Intent Analyzer** | Psychological intent analysis & deception detection | `intent` |
|
|
430
435
|
|
|
431
436
|
### Quick Usage Examples
|
|
432
437
|
|
|
@@ -445,6 +450,11 @@ extractor doc.txt --iterate 3 --mode thorough --verbose
|
|
|
445
450
|
judge essay.txt --criteria clarity,accuracy,coherence --context "academic writing"
|
|
446
451
|
judge code.py --context "code review" --format plain --verbose
|
|
447
452
|
judge proposal.md --custom-criteria has_examples,covers_risks --output assessment.json
|
|
453
|
+
|
|
454
|
+
# Intent analysis with psychological insights and deception detection
|
|
455
|
+
intent conversation.txt --focus-participant user --depth comprehensive
|
|
456
|
+
intent email.txt --format plain --context document --verbose
|
|
457
|
+
intent chat_log.json --conversation-mode --provider lmstudio --model qwen/qwen3-30b-a3b-2507
|
|
448
458
|
```
|
|
449
459
|
|
|
450
460
|
### Installation & Setup
|
|
@@ -459,6 +469,7 @@ pip install abstractcore[all]
|
|
|
459
469
|
summarizer --help
|
|
460
470
|
extractor --help
|
|
461
471
|
judge --help
|
|
472
|
+
intent --help
|
|
462
473
|
```
|
|
463
474
|
|
|
464
475
|
### Alternative Usage Methods
|
|
@@ -468,11 +479,13 @@ judge --help
|
|
|
468
479
|
summarizer document.txt
|
|
469
480
|
extractor report.pdf
|
|
470
481
|
judge essay.md
|
|
482
|
+
intent conversation.txt
|
|
471
483
|
|
|
472
484
|
# Method 2: Via Python module
|
|
473
485
|
python -m abstractcore.apps summarizer document.txt
|
|
474
486
|
python -m abstractcore.apps extractor report.pdf
|
|
475
487
|
python -m abstractcore.apps judge essay.md
|
|
488
|
+
python -m abstractcore.apps intent conversation.txt
|
|
476
489
|
```
|
|
477
490
|
|
|
478
491
|
### Key Parameters
|
|
@@ -514,10 +527,11 @@ python -m abstractcore.apps judge essay.md
|
|
|
514
527
|
|
|
515
528
|
### Full Documentation
|
|
516
529
|
|
|
517
|
-
Each application has
|
|
530
|
+
Each application has documentation with examples and usage information:
|
|
518
531
|
|
|
519
532
|
- **[Summarizer Guide](docs/apps/basic-summarizer.md)** - Document summarization with multiple strategies
|
|
520
533
|
- **[Extractor Guide](docs/apps/basic-extractor.md)** - Entity and relationship extraction
|
|
534
|
+
- **[Intent Analyzer Guide](docs/apps/basic-intent.md)** - Psychological intent analysis and deception detection
|
|
521
535
|
- **[Judge Guide](docs/apps/basic-judge.md)** - Text evaluation and scoring systems
|
|
522
536
|
|
|
523
537
|
**When to use the apps:**
|
|
@@ -551,6 +565,10 @@ abstractcore --set-console-log-level NONE # Disable console logging
|
|
|
551
565
|
abstractcore --enable-file-logging # Save logs to files
|
|
552
566
|
abstractcore --enable-debug-logging # Full debug mode
|
|
553
567
|
|
|
568
|
+
# Configure vision for image analysis with text-only models
|
|
569
|
+
abstractcore --set-vision-provider ollama qwen2.5vl:7b
|
|
570
|
+
abstractcore --set-vision-provider lmstudio qwen/qwen3-vl-4b
|
|
571
|
+
|
|
554
572
|
# Set API keys as needed
|
|
555
573
|
abstractcore --set-api-key openai sk-your-key-here
|
|
556
574
|
abstractcore --set-api-key anthropic your-anthropic-key
|
|
@@ -662,7 +680,7 @@ llm = create_llm("anthropic", model="claude-3.5-sonnet")
|
|
|
662
680
|
response = llm.generate(analysis_prompt, media=documents)
|
|
663
681
|
|
|
664
682
|
# Automatic format handling:
|
|
665
|
-
# - PDF:
|
|
683
|
+
# - PDF: Text extraction with PyMuPDF4LLM
|
|
666
684
|
# - Excel: Table parsing with pandas
|
|
667
685
|
# - PowerPoint: Slide content extraction with unstructured
|
|
668
686
|
```
|
|
@@ -715,6 +733,8 @@ review = llm.generate(
|
|
|
715
733
|
print(f"{review.title}: {review.rating}/5")
|
|
716
734
|
```
|
|
717
735
|
|
|
736
|
+
[Learn more about Structured Output](docs/structured-output.md)
|
|
737
|
+
|
|
718
738
|
### 7. Universal API Server
|
|
719
739
|
|
|
720
740
|
```bash
|
|
@@ -770,7 +790,7 @@ pip install abstractcore[all]
|
|
|
770
790
|
|
|
771
791
|
**Media processing extras:**
|
|
772
792
|
```bash
|
|
773
|
-
# For
|
|
793
|
+
# For PDF processing
|
|
774
794
|
pip install pymupdf4llm
|
|
775
795
|
|
|
776
796
|
# For Office documents (DOCX, XLSX, PPT)
|
|
@@ -1,25 +1,28 @@
|
|
|
1
1
|
abstractcore/__init__.py,sha256=A7Gbn_C-robdWLLQXjtTyWsoaXDGIblSFmLRV_ni6O8,1934
|
|
2
2
|
abstractcore/apps/__init__.py,sha256=sgNOv3lYyOWNBC-w6GnRN6aILGCbdkQtNcuQdJz5ghE,31
|
|
3
|
-
abstractcore/apps/__main__.py,sha256=
|
|
3
|
+
abstractcore/apps/__main__.py,sha256=fV9cGU99K4lGRsPNOLkh8qrDjH3JtMEWNlBiZrvI5Kk,1974
|
|
4
4
|
abstractcore/apps/app_config_utils.py,sha256=5GIvXnD996LFIV3-BpfkqII6UqYlStm7ZCgmqDEN8dc,890
|
|
5
|
+
abstractcore/apps/deepsearch.py,sha256=UlmuBS9T4yNsz0V_iY08GNNDTstsI5OJNNV6c8CU6AE,23191
|
|
5
6
|
abstractcore/apps/extractor.py,sha256=OfiqB9l_alH9xCGb6zOD__QJkDjdKOlLZngriVgmn7c,23749
|
|
7
|
+
abstractcore/apps/intent.py,sha256=5ie_H9_K_ZxlA0oCu7ROUrsgwfzDNFgVUyBNec6YVRE,22813
|
|
6
8
|
abstractcore/apps/judge.py,sha256=nOgxvn-BbhNY6xU9AlTeD1yidTh73AiVlSN7hQCVE2M,23169
|
|
7
9
|
abstractcore/apps/summarizer.py,sha256=9aD6KH21w-tv_wGp9MaO2uyJuaU71OemW7KpqrG5t6w,14669
|
|
8
10
|
abstractcore/architectures/__init__.py,sha256=-4JucAM7JkMWShWKkePoclxrUHRKgaG36UTguJihE0U,1046
|
|
9
11
|
abstractcore/architectures/detection.py,sha256=Cxap1pL6qOJjyY22Vc4hzbLALTuuBisPT5UaMotousk,10025
|
|
10
12
|
abstractcore/architectures/enums.py,sha256=9vIv2vDBEKhxwzwH9iaSAyf-iVj3p8y9loMeN_mYTJ8,3821
|
|
11
13
|
abstractcore/assets/architecture_formats.json,sha256=CIf6SaR_IJs1D7Uvd1K3zWngIXJ_yq3DM_IE3wnpCHY,16076
|
|
12
|
-
abstractcore/assets/model_capabilities.json,sha256=
|
|
14
|
+
abstractcore/assets/model_capabilities.json,sha256=AJwF1tmaGGl4P-UTbKX6awsgw7W543UN2TuqhC3y_Ss,50236
|
|
13
15
|
abstractcore/assets/session_schema.json,sha256=hMCVrq3KSyVExrMGzuykf7bU-z6WyIVuEGU8du9_zUY,10570
|
|
14
|
-
abstractcore/
|
|
15
|
-
abstractcore/
|
|
16
|
-
abstractcore/
|
|
16
|
+
abstractcore/config/__init__.py,sha256=4mHX5z5Sq8R8xh78tb9jjZLaz_oBNW1eh914OsdDTxs,318
|
|
17
|
+
abstractcore/config/main.py,sha256=U9zz-h9OvadqW9pBRRf999gVZKtBWsk27oPJtiJa8Qk,32646
|
|
18
|
+
abstractcore/config/manager.py,sha256=mo-cpT-jNfQmfTSe9LcalwE1WqjCyvNQexPIqNMS6sc,13098
|
|
19
|
+
abstractcore/config/vision_config.py,sha256=jJzO4zBexh8SqSKp6YKOXdMDSv4AL4Ztl5Xi-5c4KyY,17869
|
|
17
20
|
abstractcore/core/__init__.py,sha256=2h-86U4QkCQ4gzZ4iRusSTMlkODiUS6tKjZHiEXz6rM,684
|
|
18
21
|
abstractcore/core/enums.py,sha256=BhkVnHC-X1_377JDmqd-2mnem9GdBLqixWlYzlP_FJU,695
|
|
19
22
|
abstractcore/core/factory.py,sha256=ec7WGW2JKK-dhDplziTAeRkebEUFymtEEZ_bS5qkpqY,2798
|
|
20
23
|
abstractcore/core/interface.py,sha256=-VAY0nlsTnWN_WghiuMC7iE7xUdZfYOg6KlgrAPi14Y,14086
|
|
21
24
|
abstractcore/core/retry.py,sha256=wNlUAxfmvdO_uVWb4iqkhTqd7O1oRwXxqvVQaLXQOw0,14538
|
|
22
|
-
abstractcore/core/session.py,sha256=
|
|
25
|
+
abstractcore/core/session.py,sha256=thqG8zWICKpPeCJi5IoKbMhBDOH4RJlTfUjymP14TFY,38249
|
|
23
26
|
abstractcore/core/types.py,sha256=jj44i07kMjdg9FQ3mA_fK6r_M0Lcgt1RQpy1Ra5w-eI,4578
|
|
24
27
|
abstractcore/embeddings/__init__.py,sha256=hR3xZyqcRm4c2pq1dIa5lxj_-Bk70Zad802JQN4joWo,637
|
|
25
28
|
abstractcore/embeddings/manager.py,sha256=uFVbRPHx_R-kVMVA7N7_7EzeUmCJCeN9Dv0EV8Jko24,52245
|
|
@@ -43,27 +46,29 @@ abstractcore/media/processors/pdf_processor.py,sha256=qniYt7cTYYPVRi_cS1IsXztOld
|
|
|
43
46
|
abstractcore/media/processors/text_processor.py,sha256=E28FtT2_jzsvMIDwZi6aVWuu_pSyAPSBa96fe4YYcU8,21092
|
|
44
47
|
abstractcore/media/utils/__init__.py,sha256=30-CTif91iRKOXJ4njGiduWAt-xp31U7NafMBNvgdO0,460
|
|
45
48
|
abstractcore/media/utils/image_scaler.py,sha256=QrYqoNQc8tzGu7I9Sf_E-Iv7ei2oLh714AGiX3yACNM,11338
|
|
46
|
-
abstractcore/processing/__init__.py,sha256=
|
|
49
|
+
abstractcore/processing/__init__.py,sha256=QcACEnhnHKYCkFL1LNOW_uqBrwkTAmz5A61N4K2dyu0,988
|
|
50
|
+
abstractcore/processing/basic_deepsearch.py,sha256=dzJQtH4k44XY9tvG0Z4JIlYt_s7HpbLdSPScha-t7vk,101036
|
|
47
51
|
abstractcore/processing/basic_extractor.py,sha256=3x-3BdIHgLvqLnLF6K1-P4qVaLIpAnNIIutaJi7lDQM,49832
|
|
52
|
+
abstractcore/processing/basic_intent.py,sha256=wD99Z7fE2RiYk6oyTZXojUbv-bz8HhKFIuIHYLLTw54,32455
|
|
48
53
|
abstractcore/processing/basic_judge.py,sha256=tKWJrg_tY4vCHzWgXxz0ZjgLXBYYfpMcpG7vl03hJcM,32218
|
|
49
54
|
abstractcore/processing/basic_summarizer.py,sha256=XHNxMQ_8aLStTeUo6_2JaThlct12Htpz7ORmm0iuJsg,25495
|
|
50
55
|
abstractcore/providers/__init__.py,sha256=n-2RMNm3QpKxHw9EOjv8icRMRnfJp5Xg0uSVzHCW3BI,1222
|
|
51
|
-
abstractcore/providers/anthropic_provider.py,sha256=
|
|
52
|
-
abstractcore/providers/base.py,sha256=
|
|
53
|
-
abstractcore/providers/huggingface_provider.py,sha256=
|
|
54
|
-
abstractcore/providers/lmstudio_provider.py,sha256=
|
|
55
|
-
abstractcore/providers/mlx_provider.py,sha256=
|
|
56
|
-
abstractcore/providers/ollama_provider.py,sha256=
|
|
57
|
-
abstractcore/providers/openai_provider.py,sha256=
|
|
58
|
-
abstractcore/providers/registry.py,sha256=
|
|
56
|
+
abstractcore/providers/anthropic_provider.py,sha256=wXzRgW1AJCRX5dovXHVRTNxL96vycr7Ug9omKnLjPzo,21588
|
|
57
|
+
abstractcore/providers/base.py,sha256=gWlkchUZBXgcJAKNV8U1hROUGGw8MdDIuLHQM2LRx8Y,51530
|
|
58
|
+
abstractcore/providers/huggingface_provider.py,sha256=mTjpdZlC49QCMqsDHEabTnQbX21z7MNV77Dv9hs_RiM,53696
|
|
59
|
+
abstractcore/providers/lmstudio_provider.py,sha256=jzNMngjRtpw4lgdF66i5h22QIPWXvritezcJndetu7A,21632
|
|
60
|
+
abstractcore/providers/mlx_provider.py,sha256=1gI1KXJdKcgjiaivk7LpuBScpYgsVJ9YEikm-gCChoo,22156
|
|
61
|
+
abstractcore/providers/ollama_provider.py,sha256=VfPn6y87Jjih1MhFsHhijtv1AhbefhwSh4_Z2eAKc4o,21975
|
|
62
|
+
abstractcore/providers/openai_provider.py,sha256=kV1YXjzKyqJMQhmn9kG7AwQ-ssu8PipFwXdmmuosR98,23346
|
|
63
|
+
abstractcore/providers/registry.py,sha256=oqEaKjF2nuPiw9W0u6DM7TJgm4P2i94aKZRb8Ma6PqM,16002
|
|
59
64
|
abstractcore/providers/streaming.py,sha256=VnffBV_CU9SAKzghL154OoFyEdDsiLwUNXPahyU41Bw,31342
|
|
60
65
|
abstractcore/server/__init__.py,sha256=1DSAz_YhQtnKv7sNi5TMQV8GFujctDOabgvAdilQE0o,249
|
|
61
66
|
abstractcore/server/app.py,sha256=7pG5ZkZqYNnyby4jyvp3_NKl5nNDmZpOhv_-F8Jruy4,96580
|
|
62
67
|
abstractcore/structured/__init__.py,sha256=VXRQHGcm-iaYnLOBPin2kyhvhhQA0kaGt_pcNDGsE_8,339
|
|
63
|
-
abstractcore/structured/handler.py,sha256=
|
|
68
|
+
abstractcore/structured/handler.py,sha256=hcUe_fZcwx0O3msLqFiOsj6-jbq3S-ZQa9c1nRIZvuo,24622
|
|
64
69
|
abstractcore/structured/retry.py,sha256=BN_PvrWybyU1clMy2cult1-TVxFSMaVqiCPmmXvA5aI,3805
|
|
65
70
|
abstractcore/tools/__init__.py,sha256=oh6vG0RdM1lqUtOp95mLrTsWLh9VmhJf5_FVjGIP5_M,2259
|
|
66
|
-
abstractcore/tools/common_tools.py,sha256=
|
|
71
|
+
abstractcore/tools/common_tools.py,sha256=sfCuwZX3mG229yZRAgaLlDrVpGCVfdH_Uq5tZUVw6n8,95122
|
|
67
72
|
abstractcore/tools/core.py,sha256=lUUGihyceiRYlKUFvEMig9jWFF563d574mSDbYYD3fM,4777
|
|
68
73
|
abstractcore/tools/handler.py,sha256=GmDenXAJkhceWSGlhvuF90aMb2301tRTh6WxGwBQifc,12022
|
|
69
74
|
abstractcore/tools/parser.py,sha256=1r5nmEEp1Rid3JU6ct-s3lP-eCln67fvXG5HCjqiRew,27740
|
|
@@ -71,15 +76,15 @@ abstractcore/tools/registry.py,sha256=cN3nbPEK6L2vAd9740MIFf2fitW_4WHpQfK4KvQjnT
|
|
|
71
76
|
abstractcore/tools/syntax_rewriter.py,sha256=c3NSTvUF3S3ho5Cwjp7GJJdibeYAI6k3iaBwhKcpTfo,17318
|
|
72
77
|
abstractcore/tools/tag_rewriter.py,sha256=UGFMBj2QKwm12j8JQ6oc2C_N3ZeNqz9Enz4VkEIrS0c,20338
|
|
73
78
|
abstractcore/utils/__init__.py,sha256=29mMc3jV_suEPBn7W8Yw_wqcdvFP-083ws5AUFJVM28,676
|
|
74
|
-
abstractcore/utils/cli.py,sha256=
|
|
79
|
+
abstractcore/utils/cli.py,sha256=tO7S-iVSZavxJaX7OTruNAmF3lJz9eCDORK8Dm7FdgA,70558
|
|
75
80
|
abstractcore/utils/message_preprocessor.py,sha256=GdHkm6tmrgjm3PwHRSCjIsq1XLkbhy_vDEKEUE7OiKY,6028
|
|
76
81
|
abstractcore/utils/self_fixes.py,sha256=QEDwNTW80iQM4ftfEY3Ghz69F018oKwLM9yeRCYZOvw,5886
|
|
77
82
|
abstractcore/utils/structured_logging.py,sha256=Vm-HviSa42G9DJCWmaEv4a0QG3NMsADD3ictLOs4En0,19952
|
|
78
83
|
abstractcore/utils/token_utils.py,sha256=eLwFmJ68p9WMFD_MHLMmeJRW6Oqx_4hKELB8FNQ2Mnk,21097
|
|
79
|
-
abstractcore/utils/version.py,sha256=
|
|
80
|
-
abstractcore-2.
|
|
81
|
-
abstractcore-2.
|
|
82
|
-
abstractcore-2.
|
|
83
|
-
abstractcore-2.
|
|
84
|
-
abstractcore-2.
|
|
85
|
-
abstractcore-2.
|
|
84
|
+
abstractcore/utils/version.py,sha256=GC9cH32Kxl_Gm71B9JyHaV8605RVj_YHzLdOS6AjFM0,605
|
|
85
|
+
abstractcore-2.5.2.dist-info/licenses/LICENSE,sha256=PI2v_4HMvd6050uDD_4AY_8PzBnu2asa3RKbdDjowTA,1078
|
|
86
|
+
abstractcore-2.5.2.dist-info/METADATA,sha256=VeTcJ285fQCo75pDmpjGmnllxlWHH6JvyfSYeN_o8Y4,32893
|
|
87
|
+
abstractcore-2.5.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
88
|
+
abstractcore-2.5.2.dist-info/entry_points.txt,sha256=jXNdzeltVs23A2JM2e2HOiAHldHrsnud3EvPI5VffOs,658
|
|
89
|
+
abstractcore-2.5.2.dist-info/top_level.txt,sha256=DiNHBI35SIawW3N9Z-z0y6cQYNbXd32pvBkW0RLfScs,13
|
|
90
|
+
abstractcore-2.5.2.dist-info/RECORD,,
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
[console_scripts]
|
|
2
|
-
abstractcore = abstractcore.
|
|
2
|
+
abstractcore = abstractcore.config.main:main
|
|
3
3
|
abstractcore-chat = abstractcore.utils.cli:main
|
|
4
|
-
abstractcore-config = abstractcore.
|
|
4
|
+
abstractcore-config = abstractcore.config.main:main
|
|
5
|
+
abstractcore-deepsearch = abstractcore.apps.deepsearch:main
|
|
5
6
|
abstractcore-extractor = abstractcore.apps.extractor:main
|
|
7
|
+
abstractcore-intent = abstractcore.apps.intent:main
|
|
6
8
|
abstractcore-judge = abstractcore.apps.judge:main
|
|
7
9
|
abstractcore-summarizer = abstractcore.apps.summarizer:main
|
|
10
|
+
deepsearch = abstractcore.apps.deepsearch:main
|
|
8
11
|
extractor = abstractcore.apps.extractor:main
|
|
12
|
+
intent = abstractcore.apps.intent:main
|
|
9
13
|
judge = abstractcore.apps.judge:main
|
|
10
14
|
summarizer = abstractcore.apps.summarizer:main
|
abstractcore/cli/__init__.py
DELETED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|