|
|
""" |
|
|
FinEE CLI - Command-line interface for financial entity extraction. |
|
|
|
|
|
Usage: |
|
|
finee extract "Rs.500 debited from A/c 1234 on 01-01-25" |
|
|
finee extract --file transactions.txt |
|
|
finee stats |
|
|
finee backends |
|
|
""" |
|
|
|
|
|
import argparse |
|
|
import json |
|
|
import sys |
|
|
from typing import Optional |
|
|
import logging |
|
|
|
|
|
from .extractor import FinEE, extract, get_extractor |
|
|
from .schema import ExtractionConfig |
|
|
from .backends import get_available_backends |
|
|
|
|
|
|
|
|
def setup_logging(verbose: bool = False): |
|
|
"""Configure logging.""" |
|
|
level = logging.DEBUG if verbose else logging.WARNING |
|
|
logging.basicConfig( |
|
|
level=level, |
|
|
format='%(levelname)s: %(message)s' |
|
|
) |
|
|
|
|
|
|
|
|
def cmd_extract(args): |
|
|
"""Handle extract command.""" |
|
|
|
|
|
if args.file: |
|
|
with open(args.file, 'r') as f: |
|
|
texts = [line.strip() for line in f if line.strip()] |
|
|
else: |
|
|
texts = [args.text] |
|
|
|
|
|
|
|
|
config = ExtractionConfig( |
|
|
use_llm=not args.no_llm, |
|
|
cache_enabled=not args.no_cache, |
|
|
) |
|
|
|
|
|
extractor = FinEE(config) |
|
|
|
|
|
|
|
|
for text in texts: |
|
|
result = extractor.extract(text) |
|
|
|
|
|
if args.json: |
|
|
print(result.to_json()) |
|
|
else: |
|
|
print(f"\n{'='*60}") |
|
|
print(f"Input: {text[:80]}{'...' if len(text) > 80 else ''}") |
|
|
print(f"{'='*60}") |
|
|
|
|
|
|
|
|
print(f"Amount: {result.amount}") |
|
|
print(f"Type: {result.type.value if result.type else 'N/A'}") |
|
|
print(f"Date: {result.date or 'N/A'}") |
|
|
print(f"Account: {result.account or 'N/A'}") |
|
|
print(f"Reference: {result.reference or 'N/A'}") |
|
|
|
|
|
|
|
|
print(f"Merchant: {result.merchant or 'N/A'}") |
|
|
print(f"Category: {result.category.value if result.category else 'N/A'}") |
|
|
|
|
|
|
|
|
print(f"\nConfidence: {result.confidence.value} ({result.confidence_score:.0%})") |
|
|
print(f"Time: {result.processing_time_ms:.2f}ms") |
|
|
print(f"Cached: {result.from_cache}") |
|
|
|
|
|
|
|
|
def cmd_stats(args): |
|
|
"""Handle stats command.""" |
|
|
extractor = get_extractor() |
|
|
stats = extractor.get_stats() |
|
|
|
|
|
print("\nFinEE Statistics") |
|
|
print("="*40) |
|
|
print(json.dumps(stats, indent=2)) |
|
|
|
|
|
|
|
|
def cmd_backends(args): |
|
|
"""Handle backends command.""" |
|
|
backends = get_available_backends() |
|
|
|
|
|
print("\nAvailable Backends") |
|
|
print("="*40) |
|
|
|
|
|
if backends: |
|
|
for backend in backends: |
|
|
print(f" ✅ {backend}") |
|
|
else: |
|
|
print(" ⚠️ No LLM backends available") |
|
|
print("\nInstall a backend:") |
|
|
print(" pip install finee[metal] # Apple Silicon") |
|
|
print(" pip install finee[cuda] # NVIDIA GPU") |
|
|
print(" pip install finee[cpu] # CPU (llama.cpp)") |
|
|
|
|
|
|
|
|
def cmd_version(args): |
|
|
"""Handle version command.""" |
|
|
from . import __version__ |
|
|
print(f"finee {__version__}") |
|
|
|
|
|
|
|
|
def main(): |
|
|
"""Main CLI entry point.""" |
|
|
parser = argparse.ArgumentParser( |
|
|
prog='finee', |
|
|
description='Extract structured financial entities from Indian banking messages' |
|
|
) |
|
|
parser.add_argument('-v', '--verbose', action='store_true', help='Verbose output') |
|
|
parser.add_argument('--version', action='store_true', help='Show version') |
|
|
|
|
|
subparsers = parser.add_subparsers(dest='command', help='Commands') |
|
|
|
|
|
|
|
|
extract_parser = subparsers.add_parser('extract', help='Extract entities from text') |
|
|
extract_parser.add_argument('text', nargs='?', help='Transaction text') |
|
|
extract_parser.add_argument('-f', '--file', help='Read from file (one per line)') |
|
|
extract_parser.add_argument('--json', action='store_true', help='Output as JSON') |
|
|
extract_parser.add_argument('--no-llm', action='store_true', help='Disable LLM (regex only)') |
|
|
extract_parser.add_argument('--no-cache', action='store_true', help='Disable caching') |
|
|
extract_parser.set_defaults(func=cmd_extract) |
|
|
|
|
|
|
|
|
stats_parser = subparsers.add_parser('stats', help='Show extraction statistics') |
|
|
stats_parser.set_defaults(func=cmd_stats) |
|
|
|
|
|
|
|
|
backends_parser = subparsers.add_parser('backends', help='List available backends') |
|
|
backends_parser.set_defaults(func=cmd_backends) |
|
|
|
|
|
|
|
|
args = parser.parse_args() |
|
|
|
|
|
|
|
|
setup_logging(args.verbose) |
|
|
|
|
|
|
|
|
if args.version: |
|
|
cmd_version(args) |
|
|
return |
|
|
|
|
|
|
|
|
if hasattr(args, 'func'): |
|
|
|
|
|
if args.command == 'extract': |
|
|
if not args.text and not args.file: |
|
|
extract_parser.error("Either TEXT or --file is required") |
|
|
|
|
|
args.func(args) |
|
|
else: |
|
|
parser.print_help() |
|
|
|
|
|
|
|
|
if __name__ == '__main__': |
|
|
main() |
|
|
|