File size: 5,030 Bytes
2197ab7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5b40ec9
 
2197ab7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5b40ec9
2197ab7
 
 
5b40ec9
 
2197ab7
 
 
5b40ec9
2197ab7
5b40ec9
2197ab7
5b40ec9
2197ab7
5b40ec9
2197ab7
 
 
 
 
 
 
 
 
5b40ec9
2197ab7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
"""Configuration management for fabric-to-espanso."""
# TODO: check if config.validate wel gebruikt wordt

from typing import Dict, Any, Optional
from dataclasses import dataclass, field
from urllib.parse import urlparse
from pathlib import Path
import logging

from parameters import (
    FABRIC_PATTERNS_FOLDER,
    YAML_OUTPUT_FOLDER,
    DEFAULT_TRIGGER,
    OBSIDIAN_OUTPUT_FOLDER,
    OBSIDIAN_INPUT_FOLDER,
    BASE_WORDS,
    QDRANT_URL,
    USE_FASTEMBED,
    EMBED_MODEL_DENSE,
    EMBED_MODEL_SPARSE,
    COLLECTION_NAME,
    REQUIRED_FIELDS,
    REQUIRED_FIELDS_DEFAULTS
)

logger = logging.getLogger('fabric_to_espanso')

@dataclass
class DatabaseConfig:
    """Database configuration settings."""
    url: str = QDRANT_URL
    max_retries: int = 3
    retry_delay: float = 1.0
    timeout: float = 10.0
    api_key: Optional[str] = None
    required_fields: list = field(default_factory=lambda: REQUIRED_FIELDS)
    required_fields_defaults: dict = field(default_factory=lambda: REQUIRED_FIELDS_DEFAULTS)


    def validate(self) -> None:
        """Validate the database configuration.
        
        Raises:
            ConfigurationError: If any configuration values are invalid.
        """
        try:
            result = urlparse(self.url)
            if not all([result.scheme, result.netloc]):
                raise ValueError(f"Invalid database URL: {self.url}")
            
            if self.max_retries < 0:
                raise ValueError(f"max_retries must be >= 0, got {self.max_retries}")
            
            if self.retry_delay <= 0:
                raise ValueError(f"retry_delay must be > 0, got {self.retry_delay}")
            
            if self.timeout <= 0:
                raise ValueError(f"timeout must be > 0, got {self.timeout}")
                
        except ValueError as e:
            from .exceptions import ConfigurationError
            raise ConfigurationError(str(e))

@dataclass
class EmbeddingModelConfig:
    """Embedding model configuration."""
    use_fastembed: bool = USE_FASTEMBED
    collection_name: str = COLLECTION_NAME
    dense_model_name: str = EMBED_MODEL_DENSE
    sparse_model_name: str = EMBED_MODEL_SPARSE
    
    def validate(self) -> None:
        """Validate the embedding configuration."""
        if not self.dense_model_name:
            from .exceptions import ConfigurationError
            raise ConfigurationError("Dense Embedding model name cannot be empty")
        
        if not self.sparse_model_name:
            from .exceptions import ConfigurationError
            raise ConfigurationError("Sparse Embedding model name cannot be empty")

class Config:
    """Global configuration singleton."""
    _instance: Optional['Config'] = None
    
    def __new__(cls) -> 'Config':
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance.database = DatabaseConfig()
            cls._instance.embedding = EmbeddingModelConfig()
            cls._instance.espanso_trigger = DEFAULT_TRIGGER
            cls._instance.fabric_patterns_folder = FABRIC_PATTERNS_FOLDER
            cls._instance.yaml_output_folder = YAML_OUTPUT_FOLDER
            cls._instance.obsidian_output_folder = OBSIDIAN_OUTPUT_FOLDER
            cls._instance.obsidian_input_folder = OBSIDIAN_INPUT_FOLDER
            cls._instance.base_words = BASE_WORDS
        return cls._instance
    
    def validate(self) -> None:
        """Validate all configuration settings."""
        self.database.validate()
        self.embedding.validate()
        
        # Validate paths
        if not self.espanso_trigger:
            from .exceptions import ConfigurationError
            raise ConfigurationError("The default trigger for espanso patterns cannot be empty")

        if not self.fabric_patterns_folder:
            from .exceptions import ConfigurationError
            raise ConfigurationError("The fabric patterns folder path cannot be empty")
            
        if not self.yaml_output_folder:
            from .exceptions import ConfigurationError
            raise ConfigurationError("YAML output folder path for espanso cannot be empty")

        if not self.obsidian_output_folder:
            from .exceptions import ConfigurationError
            raise ConfigurationError("Obsidian output folder path to write the files for Obsidian Textgenerator cannot be empty")
        
        if not self.obsidian_input_folder:
            from .exceptions import ConfigurationError
            raise ConfigurationError("Obsidian input folder path to find the personal prompts stored in Obsidian cannot be empty")

        for path in [self.fabric_patterns_folder, self.yaml_output_folder, self.obsidian_output_folder, self.obsidian_input_folder]:
            if not path == "cloud_dummy" and not Path(path).is_dir():
                from .exceptions import ConfigurationError
                raise ConfigurationError(f"{path} is not a valid directory")
# Global configuration instance
config = Config()