ebk 0.4.4__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.
Files changed (87) hide show
  1. ebk/__init__.py +35 -0
  2. ebk/ai/__init__.py +23 -0
  3. ebk/ai/knowledge_graph.py +450 -0
  4. ebk/ai/llm_providers/__init__.py +26 -0
  5. ebk/ai/llm_providers/anthropic.py +209 -0
  6. ebk/ai/llm_providers/base.py +295 -0
  7. ebk/ai/llm_providers/gemini.py +285 -0
  8. ebk/ai/llm_providers/ollama.py +294 -0
  9. ebk/ai/metadata_enrichment.py +394 -0
  10. ebk/ai/question_generator.py +328 -0
  11. ebk/ai/reading_companion.py +224 -0
  12. ebk/ai/semantic_search.py +433 -0
  13. ebk/ai/text_extractor.py +393 -0
  14. ebk/calibre_import.py +66 -0
  15. ebk/cli.py +6433 -0
  16. ebk/config.py +230 -0
  17. ebk/db/__init__.py +37 -0
  18. ebk/db/migrations.py +507 -0
  19. ebk/db/models.py +725 -0
  20. ebk/db/session.py +144 -0
  21. ebk/decorators.py +1 -0
  22. ebk/exports/__init__.py +0 -0
  23. ebk/exports/base_exporter.py +218 -0
  24. ebk/exports/echo_export.py +279 -0
  25. ebk/exports/html_library.py +1743 -0
  26. ebk/exports/html_utils.py +87 -0
  27. ebk/exports/hugo.py +59 -0
  28. ebk/exports/jinja_export.py +286 -0
  29. ebk/exports/multi_facet_export.py +159 -0
  30. ebk/exports/opds_export.py +232 -0
  31. ebk/exports/symlink_dag.py +479 -0
  32. ebk/exports/zip.py +25 -0
  33. ebk/extract_metadata.py +341 -0
  34. ebk/ident.py +89 -0
  35. ebk/library_db.py +1440 -0
  36. ebk/opds.py +748 -0
  37. ebk/plugins/__init__.py +42 -0
  38. ebk/plugins/base.py +502 -0
  39. ebk/plugins/hooks.py +442 -0
  40. ebk/plugins/registry.py +499 -0
  41. ebk/repl/__init__.py +9 -0
  42. ebk/repl/find.py +126 -0
  43. ebk/repl/grep.py +173 -0
  44. ebk/repl/shell.py +1677 -0
  45. ebk/repl/text_utils.py +320 -0
  46. ebk/search_parser.py +413 -0
  47. ebk/server.py +3608 -0
  48. ebk/services/__init__.py +28 -0
  49. ebk/services/annotation_extraction.py +351 -0
  50. ebk/services/annotation_service.py +380 -0
  51. ebk/services/export_service.py +577 -0
  52. ebk/services/import_service.py +447 -0
  53. ebk/services/personal_metadata_service.py +347 -0
  54. ebk/services/queue_service.py +253 -0
  55. ebk/services/tag_service.py +281 -0
  56. ebk/services/text_extraction.py +317 -0
  57. ebk/services/view_service.py +12 -0
  58. ebk/similarity/__init__.py +77 -0
  59. ebk/similarity/base.py +154 -0
  60. ebk/similarity/core.py +471 -0
  61. ebk/similarity/extractors.py +168 -0
  62. ebk/similarity/metrics.py +376 -0
  63. ebk/skills/SKILL.md +182 -0
  64. ebk/skills/__init__.py +1 -0
  65. ebk/vfs/__init__.py +101 -0
  66. ebk/vfs/base.py +298 -0
  67. ebk/vfs/library_vfs.py +122 -0
  68. ebk/vfs/nodes/__init__.py +54 -0
  69. ebk/vfs/nodes/authors.py +196 -0
  70. ebk/vfs/nodes/books.py +480 -0
  71. ebk/vfs/nodes/files.py +155 -0
  72. ebk/vfs/nodes/metadata.py +385 -0
  73. ebk/vfs/nodes/root.py +100 -0
  74. ebk/vfs/nodes/similar.py +165 -0
  75. ebk/vfs/nodes/subjects.py +184 -0
  76. ebk/vfs/nodes/tags.py +371 -0
  77. ebk/vfs/resolver.py +228 -0
  78. ebk/vfs_router.py +275 -0
  79. ebk/views/__init__.py +32 -0
  80. ebk/views/dsl.py +668 -0
  81. ebk/views/service.py +619 -0
  82. ebk-0.4.4.dist-info/METADATA +755 -0
  83. ebk-0.4.4.dist-info/RECORD +87 -0
  84. ebk-0.4.4.dist-info/WHEEL +5 -0
  85. ebk-0.4.4.dist-info/entry_points.txt +2 -0
  86. ebk-0.4.4.dist-info/licenses/LICENSE +21 -0
  87. ebk-0.4.4.dist-info/top_level.txt +1 -0
ebk/config.py ADDED
@@ -0,0 +1,230 @@
1
+ """
2
+ Configuration management for EBK.
3
+
4
+ Handles loading and saving user configuration from:
5
+ - XDG config directory: ~/.config/ebk/config.json
6
+ - Fallback: ~/.ebk/config.json
7
+ - Legacy: ~/.ebkrc (for backward compatibility)
8
+ """
9
+
10
+ import json
11
+ from pathlib import Path
12
+ from typing import Dict, Any, Optional
13
+ from dataclasses import dataclass, asdict, field
14
+
15
+
16
+ @dataclass
17
+ class LLMConfig:
18
+ """LLM provider configuration."""
19
+ provider: str = "ollama"
20
+ model: str = "llama3.2"
21
+ host: str = "localhost"
22
+ port: int = 11434
23
+ api_key: Optional[str] = None
24
+ temperature: float = 0.7
25
+ max_tokens: Optional[int] = None
26
+
27
+
28
+ @dataclass
29
+ class ServerConfig:
30
+ """Web server configuration."""
31
+ host: str = "0.0.0.0"
32
+ port: int = 8000
33
+ auto_open_browser: bool = False
34
+ page_size: int = 50
35
+
36
+
37
+ @dataclass
38
+ class CLIConfig:
39
+ """CLI default options."""
40
+ verbose: bool = False
41
+ color: bool = True
42
+ page_size: int = 50
43
+
44
+
45
+ @dataclass
46
+ class LibraryConfig:
47
+ """Library-related settings."""
48
+ default_path: Optional[str] = None
49
+
50
+
51
+ @dataclass
52
+ class EBKConfig:
53
+ """Main EBK configuration."""
54
+ llm: LLMConfig = field(default_factory=LLMConfig)
55
+ server: ServerConfig = field(default_factory=ServerConfig)
56
+ cli: CLIConfig = field(default_factory=CLIConfig)
57
+ library: LibraryConfig = field(default_factory=LibraryConfig)
58
+
59
+ def to_dict(self) -> Dict[str, Any]:
60
+ """Convert to dictionary."""
61
+ return {
62
+ "llm": asdict(self.llm),
63
+ "server": asdict(self.server),
64
+ "cli": asdict(self.cli),
65
+ "library": asdict(self.library),
66
+ }
67
+
68
+ @classmethod
69
+ def from_dict(cls, data: Dict[str, Any]) -> 'EBKConfig':
70
+ """Create from dictionary."""
71
+ llm_data = data.get("llm", {})
72
+ server_data = data.get("server", {})
73
+ cli_data = data.get("cli", {})
74
+ library_data = data.get("library", {})
75
+ return cls(
76
+ llm=LLMConfig(**llm_data),
77
+ server=ServerConfig(**server_data),
78
+ cli=CLIConfig(**cli_data),
79
+ library=LibraryConfig(**library_data),
80
+ )
81
+
82
+
83
+ def get_config_path() -> Path:
84
+ """
85
+ Get configuration file path.
86
+
87
+ Follows XDG Base Directory specification:
88
+ 1. $XDG_CONFIG_HOME/ebk/config.json (usually ~/.config/ebk/config.json)
89
+ 2. Fallback: ~/.ebk/config.json
90
+
91
+ Returns:
92
+ Path to config file
93
+ """
94
+ # Try XDG config directory first
95
+ xdg_config_home = Path.home() / ".config"
96
+ if xdg_config_home.exists():
97
+ config_dir = xdg_config_home / "ebk"
98
+ else:
99
+ # Fallback to ~/.ebk
100
+ config_dir = Path.home() / ".ebk"
101
+
102
+ return config_dir / "config.json"
103
+
104
+
105
+ def load_config() -> EBKConfig:
106
+ """
107
+ Load configuration from file.
108
+
109
+ Returns:
110
+ EBKConfig instance with loaded values or defaults
111
+ """
112
+ config_path = get_config_path()
113
+
114
+ if not config_path.exists():
115
+ # Return default config
116
+ return EBKConfig()
117
+
118
+ try:
119
+ with open(config_path, 'r') as f:
120
+ data = json.load(f)
121
+ return EBKConfig.from_dict(data)
122
+ except (json.JSONDecodeError, OSError) as e:
123
+ print(f"Warning: Failed to load config from {config_path}: {e}")
124
+ print("Using default configuration")
125
+ return EBKConfig()
126
+
127
+
128
+ def save_config(config: EBKConfig) -> None:
129
+ """
130
+ Save configuration to file.
131
+
132
+ Args:
133
+ config: Configuration to save
134
+ """
135
+ config_path = get_config_path()
136
+
137
+ # Create directory if it doesn't exist
138
+ config_path.parent.mkdir(parents=True, exist_ok=True)
139
+
140
+ # Write config
141
+ with open(config_path, 'w') as f:
142
+ json.dump(config.to_dict(), f, indent=2)
143
+
144
+ print(f"Configuration saved to {config_path}")
145
+
146
+
147
+ def ensure_config_exists() -> Path:
148
+ """
149
+ Ensure configuration file exists, creating with defaults if not.
150
+
151
+ Returns:
152
+ Path to config file
153
+ """
154
+ config_path = get_config_path()
155
+
156
+ if not config_path.exists():
157
+ config = EBKConfig()
158
+ save_config(config)
159
+ print(f"Created default configuration at {config_path}")
160
+
161
+ return config_path
162
+
163
+
164
+ def update_config(
165
+ # LLM settings
166
+ llm_provider: Optional[str] = None,
167
+ llm_model: Optional[str] = None,
168
+ llm_host: Optional[str] = None,
169
+ llm_port: Optional[int] = None,
170
+ llm_api_key: Optional[str] = None,
171
+ llm_temperature: Optional[float] = None,
172
+ llm_max_tokens: Optional[int] = None,
173
+ # Server settings
174
+ server_host: Optional[str] = None,
175
+ server_port: Optional[int] = None,
176
+ server_auto_open: Optional[bool] = None,
177
+ server_page_size: Optional[int] = None,
178
+ # CLI settings
179
+ cli_verbose: Optional[bool] = None,
180
+ cli_color: Optional[bool] = None,
181
+ cli_page_size: Optional[int] = None,
182
+ # Library settings
183
+ library_default_path: Optional[str] = None,
184
+ ) -> None:
185
+ """
186
+ Update configuration.
187
+
188
+ Only updates provided values, leaving others unchanged.
189
+ """
190
+ config = load_config()
191
+
192
+ # Update LLM config
193
+ if llm_provider is not None:
194
+ config.llm.provider = llm_provider
195
+ if llm_model is not None:
196
+ config.llm.model = llm_model
197
+ if llm_host is not None:
198
+ config.llm.host = llm_host
199
+ if llm_port is not None:
200
+ config.llm.port = llm_port
201
+ if llm_api_key is not None:
202
+ config.llm.api_key = llm_api_key
203
+ if llm_temperature is not None:
204
+ config.llm.temperature = llm_temperature
205
+ if llm_max_tokens is not None:
206
+ config.llm.max_tokens = llm_max_tokens
207
+
208
+ # Update server config
209
+ if server_host is not None:
210
+ config.server.host = server_host
211
+ if server_port is not None:
212
+ config.server.port = server_port
213
+ if server_auto_open is not None:
214
+ config.server.auto_open_browser = server_auto_open
215
+ if server_page_size is not None:
216
+ config.server.page_size = server_page_size
217
+
218
+ # Update CLI config
219
+ if cli_verbose is not None:
220
+ config.cli.verbose = cli_verbose
221
+ if cli_color is not None:
222
+ config.cli.color = cli_color
223
+ if cli_page_size is not None:
224
+ config.cli.page_size = cli_page_size
225
+
226
+ # Update library config
227
+ if library_default_path is not None:
228
+ config.library.default_path = library_default_path
229
+
230
+ save_config(config)
ebk/db/__init__.py ADDED
@@ -0,0 +1,37 @@
1
+ """
2
+ Database module for ebk.
3
+
4
+ Provides SQLAlchemy session management and initialization.
5
+ """
6
+
7
+ from .models import (
8
+ Base, Book, Author, Subject, Identifier, File, ExtractedText,
9
+ TextChunk, Cover, Concept, BookConcept, ConceptRelation,
10
+ ReadingSession, Annotation, PersonalMetadata, Tag
11
+ )
12
+ from .session import get_session, init_db, close_db
13
+ from .migrations import run_all_migrations, check_migrations
14
+
15
+ __all__ = [
16
+ 'Base',
17
+ 'Book',
18
+ 'Author',
19
+ 'Subject',
20
+ 'Identifier',
21
+ 'File',
22
+ 'ExtractedText',
23
+ 'TextChunk',
24
+ 'Cover',
25
+ 'Concept',
26
+ 'BookConcept',
27
+ 'ConceptRelation',
28
+ 'ReadingSession',
29
+ 'Annotation',
30
+ 'PersonalMetadata',
31
+ 'Tag',
32
+ 'get_session',
33
+ 'init_db',
34
+ 'close_db',
35
+ 'run_all_migrations',
36
+ 'check_migrations'
37
+ ]