narrative-ai-framework 0.1.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.
Files changed (402) hide show
  1. config/__init__.py +31 -0
  2. config/env.example.txt +96 -0
  3. config/providers.py +454 -0
  4. config/providers.yml +621 -0
  5. narrative_ai/__init__.py +28 -0
  6. narrative_ai/api/__init__.py +0 -0
  7. narrative_ai/api/__main__.py +6 -0
  8. narrative_ai/api/app.py +415 -0
  9. narrative_ai/api/dependencies.py +484 -0
  10. narrative_ai/api/http_helpers.py +26 -0
  11. narrative_ai/api/idempotency.py +89 -0
  12. narrative_ai/api/middleware/__init__.py +0 -0
  13. narrative_ai/api/middleware/auth.py +129 -0
  14. narrative_ai/api/middleware/correlation_id.py +60 -0
  15. narrative_ai/api/middleware/error_handler.py +196 -0
  16. narrative_ai/api/middleware/jwt_user_id.py +49 -0
  17. narrative_ai/api/middleware/logging.py +51 -0
  18. narrative_ai/api/middleware/rate_limit.py +163 -0
  19. narrative_ai/api/middleware/security_headers.py +30 -0
  20. narrative_ai/api/middleware/tenant.py +1 -0
  21. narrative_ai/api/models/__init__.py +0 -0
  22. narrative_ai/api/models/audio.py +0 -0
  23. narrative_ai/api/models/companion.py +0 -0
  24. narrative_ai/api/models/entry.py +0 -0
  25. narrative_ai/api/models/error.py +0 -0
  26. narrative_ai/api/models/pagination.py +0 -0
  27. narrative_ai/api/models/search.py +0 -0
  28. narrative_ai/api/routes/__init__.py +1 -0
  29. narrative_ai/api/routes/analytics.py +137 -0
  30. narrative_ai/api/routes/auth.py +444 -0
  31. narrative_ai/api/routes/companion.py +209 -0
  32. narrative_ai/api/routes/entries.py +658 -0
  33. narrative_ai/api/routes/export.py +21 -0
  34. narrative_ai/api/routes/ingestion.py +170 -0
  35. narrative_ai/api/routes/llm.py +408 -0
  36. narrative_ai/api/routes/ocr.py +89 -0
  37. narrative_ai/api/routes/rag.py +207 -0
  38. narrative_ai/api/routes/search.py +20 -0
  39. narrative_ai/api/routes/stt.py +274 -0
  40. narrative_ai/api/routes/tts.py +324 -0
  41. narrative_ai/api/routes/vlm.py +155 -0
  42. narrative_ai/api/routes/web_intel.py +79 -0
  43. narrative_ai/api/services/__init__.py +1 -0
  44. narrative_ai/api/services/entry_processor.py +211 -0
  45. narrative_ai/application/__init__.py +6 -0
  46. narrative_ai/application/dto/__init__.py +35 -0
  47. narrative_ai/application/dto/ingestion_dto.py +52 -0
  48. narrative_ai/application/dto/llm_dto.py +83 -0
  49. narrative_ai/application/dto/rag_dto.py +101 -0
  50. narrative_ai/application/dto/stt_dto.py +56 -0
  51. narrative_ai/application/dto/tts_dto.py +61 -0
  52. narrative_ai/application/dto/vlm_dto.py +64 -0
  53. narrative_ai/application/services/__init__.py +26 -0
  54. narrative_ai/application/services/companion_chat_pipeline.py +988 -0
  55. narrative_ai/application/services/entry_service.py +475 -0
  56. narrative_ai/application/services/ingestion_service.py +50 -0
  57. narrative_ai/application/services/llm_service.py +79 -0
  58. narrative_ai/application/services/rag_service.py +94 -0
  59. narrative_ai/application/services/stt_service.py +40 -0
  60. narrative_ai/application/services/tts_service.py +59 -0
  61. narrative_ai/application/services/user_profile_service.py +109 -0
  62. narrative_ai/application/services/user_service.py +146 -0
  63. narrative_ai/application/services/vlm_service.py +54 -0
  64. narrative_ai/domain/__init__.py +6 -0
  65. narrative_ai/domain/domain_services/__init__.py +17 -0
  66. narrative_ai/domain/domain_services/ingestion_engine.py +36 -0
  67. narrative_ai/domain/domain_services/llm_engine.py +39 -0
  68. narrative_ai/domain/domain_services/rag_service.py +41 -0
  69. narrative_ai/domain/domain_services/stt_engine.py +34 -0
  70. narrative_ai/domain/domain_services/tts_engine.py +39 -0
  71. narrative_ai/domain/domain_services/vlm_engine.py +24 -0
  72. narrative_ai/domain/entities/__init__.py +21 -0
  73. narrative_ai/domain/entities/analytics.py +24 -0
  74. narrative_ai/domain/entities/audio_file.py +48 -0
  75. narrative_ai/domain/entities/conversation.py +95 -0
  76. narrative_ai/domain/entities/diary_entry.py +77 -0
  77. narrative_ai/domain/entities/user.py +63 -0
  78. narrative_ai/domain/events/__init__.py +15 -0
  79. narrative_ai/domain/events/chat_events.py +0 -0
  80. narrative_ai/domain/events/domain_event.py +17 -0
  81. narrative_ai/domain/events/entry_events.py +57 -0
  82. narrative_ai/domain/events/event_bus.py +67 -0
  83. narrative_ai/domain/events/export_events.py +0 -0
  84. narrative_ai/domain/events/search_events.py +0 -0
  85. narrative_ai/domain/exceptions/__init__.py +31 -0
  86. narrative_ai/domain/exceptions/chat_exceptions.py +0 -0
  87. narrative_ai/domain/exceptions/domain_exception.py +33 -0
  88. narrative_ai/domain/exceptions/entry_exceptions.py +44 -0
  89. narrative_ai/domain/exceptions/export_exceptions.py +0 -0
  90. narrative_ai/domain/exceptions/search_exceptions.py +0 -0
  91. narrative_ai/domain/exceptions/user_exceptions.py +37 -0
  92. narrative_ai/domain/repositories/__init__.py +20 -0
  93. narrative_ai/domain/repositories/audio_repository.py +39 -0
  94. narrative_ai/domain/repositories/conversation_repository.py +46 -0
  95. narrative_ai/domain/repositories/entry_repository.py +58 -0
  96. narrative_ai/domain/repositories/search_repository.py +47 -0
  97. narrative_ai/domain/repositories/user_repository.py +45 -0
  98. narrative_ai/domain/value_objects/__init__.py +17 -0
  99. narrative_ai/domain/value_objects/audio_metadata.py +19 -0
  100. narrative_ai/domain/value_objects/embedding.py +27 -0
  101. narrative_ai/domain/value_objects/emotion.py +28 -0
  102. narrative_ai/domain/value_objects/entry_id.py +32 -0
  103. narrative_ai/domain/value_objects/tenant_id.py +51 -0
  104. narrative_ai/domain/value_objects/user_id.py +32 -0
  105. narrative_ai/engines/__init__.py +95 -0
  106. narrative_ai/engines/analytics/__init__.py +0 -0
  107. narrative_ai/engines/analytics/config.py +0 -0
  108. narrative_ai/engines/analytics/emotion_analytics.py +0 -0
  109. narrative_ai/engines/analytics/metrics_calculator.py +0 -0
  110. narrative_ai/engines/analytics/pattern_detector.py +0 -0
  111. narrative_ai/engines/companion/__init__.py +0 -0
  112. narrative_ai/engines/companion/audio_response_builder.py +11 -0
  113. narrative_ai/engines/companion/companion_engine.py +0 -0
  114. narrative_ai/engines/companion/config.py +0 -0
  115. narrative_ai/engines/companion/conversation_manager.py +0 -0
  116. narrative_ai/engines/companion/intent_classifier.py +381 -0
  117. narrative_ai/engines/companion/intent_exemplars.yaml +89 -0
  118. narrative_ai/engines/companion/llm_client.py +0 -0
  119. narrative_ai/engines/companion/prompt_builder.py +0 -0
  120. narrative_ai/engines/companion/response_processor.py +0 -0
  121. narrative_ai/engines/embeddings/__init__.py +0 -0
  122. narrative_ai/engines/embeddings/batch_processor.py +0 -0
  123. narrative_ai/engines/embeddings/chunking.py +0 -0
  124. narrative_ai/engines/embeddings/config.py +0 -0
  125. narrative_ai/engines/embeddings/embedding_engine.py +0 -0
  126. narrative_ai/engines/embeddings/model_loader.py +0 -0
  127. narrative_ai/engines/export/__init__.py +0 -0
  128. narrative_ai/engines/export/audiobook_generator.py +0 -0
  129. narrative_ai/engines/export/config.py +0 -0
  130. narrative_ai/engines/export/epub_generator.py +0 -0
  131. narrative_ai/engines/export/export_engine.py +0 -0
  132. narrative_ai/engines/export/pdf_generator.py +0 -0
  133. narrative_ai/engines/input_processor/__init__.py +130 -0
  134. narrative_ai/engines/input_processor/api.py +169 -0
  135. narrative_ai/engines/input_processor/assembler.py +334 -0
  136. narrative_ai/engines/input_processor/audio_processor.py +339 -0
  137. narrative_ai/engines/input_processor/config.py +476 -0
  138. narrative_ai/engines/input_processor/document_extractor.py +424 -0
  139. narrative_ai/engines/input_processor/factory.py +156 -0
  140. narrative_ai/engines/input_processor/image_processor.py +280 -0
  141. narrative_ai/engines/input_processor/ingestion_config.yaml +143 -0
  142. narrative_ai/engines/input_processor/ingestion_pipeline.py +516 -0
  143. narrative_ai/engines/input_processor/input_handler.py +414 -0
  144. narrative_ai/engines/input_processor/ocr_processor.py +91 -0
  145. narrative_ai/engines/input_processor/stt_processor.py +119 -0
  146. narrative_ai/engines/input_processor/types.py +436 -0
  147. narrative_ai/engines/llm/__init__.py +253 -0
  148. narrative_ai/engines/llm/api.py +230 -0
  149. narrative_ai/engines/llm/base_llm.py +423 -0
  150. narrative_ai/engines/llm/claude_llm.py +674 -0
  151. narrative_ai/engines/llm/config.py +666 -0
  152. narrative_ai/engines/llm/conversation_manager.py +620 -0
  153. narrative_ai/engines/llm/conversation_storage.py +853 -0
  154. narrative_ai/engines/llm/deepseek_llm.py +687 -0
  155. narrative_ai/engines/llm/error_mapper.py +627 -0
  156. narrative_ai/engines/llm/gemini_llm.py +867 -0
  157. narrative_ai/engines/llm/input_processor.py +555 -0
  158. narrative_ai/engines/llm/llm_engine.py +1212 -0
  159. narrative_ai/engines/llm/llm_strategy.py +705 -0
  160. narrative_ai/engines/llm/ollama_llm.py +386 -0
  161. narrative_ai/engines/llm/openai_llm.py +773 -0
  162. narrative_ai/engines/llm/response_processor.py +535 -0
  163. narrative_ai/engines/llm/token_budget.py +116 -0
  164. narrative_ai/engines/llm/xai_llm.py +650 -0
  165. narrative_ai/engines/ocr/__init__.py +52 -0
  166. narrative_ai/engines/ocr/api.py +318 -0
  167. narrative_ai/engines/ocr/config/config.yaml +41 -0
  168. narrative_ai/engines/ocr/config.py +0 -0
  169. narrative_ai/engines/ocr/core/__init__.py +14 -0
  170. narrative_ai/engines/ocr/core/api.py +59 -0
  171. narrative_ai/engines/ocr/core/factory.py +20 -0
  172. narrative_ai/engines/ocr/core/registry.py +46 -0
  173. narrative_ai/engines/ocr/demos/streamlit_app.py +222 -0
  174. narrative_ai/engines/ocr/dewarpnet/__init__.py +5 -0
  175. narrative_ai/engines/ocr/dewarpnet/infer.py +133 -0
  176. narrative_ai/engines/ocr/dewarpnet/models/densenetccnl.py +176 -0
  177. narrative_ai/engines/ocr/dewarpnet/models/unetnc.py +82 -0
  178. narrative_ai/engines/ocr/dewarpnet/utils.py +12 -0
  179. narrative_ai/engines/ocr/filters/__init__.py +18 -0
  180. narrative_ai/engines/ocr/filters/base.py +108 -0
  181. narrative_ai/engines/ocr/filters/dewarpnet_filter.py +218 -0
  182. narrative_ai/engines/ocr/filters/dewarpnet_models/__init__.py +29 -0
  183. narrative_ai/engines/ocr/filters/dewarpnet_models/densenetccnl.py +169 -0
  184. narrative_ai/engines/ocr/filters/dewarpnet_models/unetnc.py +88 -0
  185. narrative_ai/engines/ocr/filters/docres_filter.py +295 -0
  186. narrative_ai/engines/ocr/filters/remote_docres.py +259 -0
  187. narrative_ai/engines/ocr/kaggle_server.py +666 -0
  188. narrative_ai/engines/ocr/pipeline.py +428 -0
  189. narrative_ai/engines/ocr/providers/__init__.py +3 -0
  190. narrative_ai/engines/ocr/providers/remote.py +218 -0
  191. narrative_ai/engines/ocr/requirements.txt +39 -0
  192. narrative_ai/engines/ocr/tests/__init__.py +1 -0
  193. narrative_ai/engines/ocr/tests/debug_server.py +53 -0
  194. narrative_ai/engines/ocr/tests/test_filters.py +22 -0
  195. narrative_ai/engines/ocr/tests/test_functional_api.py +59 -0
  196. narrative_ai/engines/ocr/tests/test_pipeline.py +43 -0
  197. narrative_ai/engines/ocr/tests/test_providers.py +34 -0
  198. narrative_ai/engines/ocr/tests/test_utils.py +39 -0
  199. narrative_ai/engines/ocr/tests/verify_integration.py +60 -0
  200. narrative_ai/engines/ocr/tests/verify_universal_formats.py +51 -0
  201. narrative_ai/engines/ocr/utils/docres_utils.py +109 -0
  202. narrative_ai/engines/ocr/utils/image.py +29 -0
  203. narrative_ai/engines/ocr/utils/image_processing.py +69 -0
  204. narrative_ai/engines/rag/__init__.py +49 -0
  205. narrative_ai/engines/rag/api.py +295 -0
  206. narrative_ai/engines/rag/augmentation.py +175 -0
  207. narrative_ai/engines/rag/cache.py +441 -0
  208. narrative_ai/engines/rag/config.py +185 -0
  209. narrative_ai/engines/rag/context_builder.py +195 -0
  210. narrative_ai/engines/rag/hybrid_search.py +203 -0
  211. narrative_ai/engines/rag/memory_config.yaml +64 -0
  212. narrative_ai/engines/rag/memory_manager.py +805 -0
  213. narrative_ai/engines/rag/providers/__init__.py +78 -0
  214. narrative_ai/engines/rag/providers/base.py +163 -0
  215. narrative_ai/engines/rag/providers/embeddings/__init__.py +14 -0
  216. narrative_ai/engines/rag/providers/embeddings/bge_m3.py +226 -0
  217. narrative_ai/engines/rag/providers/embeddings/cohere_provider.py +103 -0
  218. narrative_ai/engines/rag/providers/embeddings/openai_provider.py +90 -0
  219. narrative_ai/engines/rag/providers/rerankers/__init__.py +11 -0
  220. narrative_ai/engines/rag/providers/rerankers/bge_reranker.py +66 -0
  221. narrative_ai/engines/rag/providers/rerankers/cohere_reranker.py +81 -0
  222. narrative_ai/engines/rag/query_processor.py +328 -0
  223. narrative_ai/engines/rag/ranking.py +0 -0
  224. narrative_ai/engines/rag/retrieval_engine.py +0 -0
  225. narrative_ai/engines/rag/strategies/__init__.py +33 -0
  226. narrative_ai/engines/rag/strategies/base.py +97 -0
  227. narrative_ai/engines/rag/strategies/default.py +75 -0
  228. narrative_ai/engines/rag/strategies/diary.py +111 -0
  229. narrative_ai/engines/rag/text_splitter.py +389 -0
  230. narrative_ai/engines/rag/types.py +104 -0
  231. narrative_ai/engines/storage_interface.py +130 -0
  232. narrative_ai/engines/stt/__init__.py +251 -0
  233. narrative_ai/engines/stt/__main__.py +6 -0
  234. narrative_ai/engines/stt/api.py +266 -0
  235. narrative_ai/engines/stt/audio_processor.py +612 -0
  236. narrative_ai/engines/stt/base_stt.py +441 -0
  237. narrative_ai/engines/stt/config.py +663 -0
  238. narrative_ai/engines/stt/conformer_model.py +0 -0
  239. narrative_ai/engines/stt/elevenlabs_stt.py +1179 -0
  240. narrative_ai/engines/stt/emotion_detector.py +717 -0
  241. narrative_ai/engines/stt/faster_whisper_stt.py +408 -0
  242. narrative_ai/engines/stt/main.py +98 -0
  243. narrative_ai/engines/stt/stt_engine.py +1765 -0
  244. narrative_ai/engines/stt/stt_strategy.py +766 -0
  245. narrative_ai/engines/stt/vad_processor.py +579 -0
  246. narrative_ai/engines/stt/whisper_stt.py +706 -0
  247. narrative_ai/engines/text_enhancement/__init__.py +0 -0
  248. narrative_ai/engines/text_enhancement/config.py +0 -0
  249. narrative_ai/engines/text_enhancement/grammar_fixer.py +0 -0
  250. narrative_ai/engines/text_enhancement/llm_enhancer.py +0 -0
  251. narrative_ai/engines/text_enhancement/normalizer.py +0 -0
  252. narrative_ai/engines/text_enhancement/validator.py +0 -0
  253. narrative_ai/engines/tts/__init__.py +254 -0
  254. narrative_ai/engines/tts/__main__.py +6 -0
  255. narrative_ai/engines/tts/api.py +194 -0
  256. narrative_ai/engines/tts/audio_processor.py +640 -0
  257. narrative_ai/engines/tts/base_tts.py +740 -0
  258. narrative_ai/engines/tts/config.py +599 -0
  259. narrative_ai/engines/tts/elevenlabs_tts.py +871 -0
  260. narrative_ai/engines/tts/emotion_applicator.py +464 -0
  261. narrative_ai/engines/tts/main.py +113 -0
  262. narrative_ai/engines/tts/model_loader.py +0 -0
  263. narrative_ai/engines/tts/openai_tts.py +712 -0
  264. narrative_ai/engines/tts/prosody_engine.py +0 -0
  265. narrative_ai/engines/tts/streaming.py +0 -0
  266. narrative_ai/engines/tts/tts_engine.py +1140 -0
  267. narrative_ai/engines/tts/tts_strategy.py +755 -0
  268. narrative_ai/engines/tts/voice_selector.py +0 -0
  269. narrative_ai/engines/vlm/__init__.py +5 -0
  270. narrative_ai/engines/vlm/api.py +106 -0
  271. narrative_ai/engines/vlm/base.py +49 -0
  272. narrative_ai/engines/vlm/config.py +70 -0
  273. narrative_ai/engines/vlm/factory.py +49 -0
  274. narrative_ai/engines/vlm/processor.py +306 -0
  275. narrative_ai/engines/vlm/providers/google.py +121 -0
  276. narrative_ai/engines/vlm/providers/ollama_cloud.py +112 -0
  277. narrative_ai/engines/vlm/providers/ollama_local.py +90 -0
  278. narrative_ai/engines/vlm/providers/openai.py +97 -0
  279. narrative_ai/engines/vlm/types.py +38 -0
  280. narrative_ai/engines/vlm/utils.py +44 -0
  281. narrative_ai/engines/voice_mode/__init__.py +36 -0
  282. narrative_ai/engines/voice_mode/agent.py +244 -0
  283. narrative_ai/engines/voice_mode/api.py +42 -0
  284. narrative_ai/engines/voice_mode/config.py +177 -0
  285. narrative_ai/engines/voice_mode/constants.py +22 -0
  286. narrative_ai/engines/voice_mode/conversation.py +419 -0
  287. narrative_ai/engines/voice_mode/llm_adapter.py +348 -0
  288. narrative_ai/engines/voice_mode/main.py +37 -0
  289. narrative_ai/engines/voice_mode/plugins/__init__.py +23 -0
  290. narrative_ai/engines/voice_mode/plugins/llm.py +288 -0
  291. narrative_ai/engines/voice_mode/plugins/stt.py +485 -0
  292. narrative_ai/engines/voice_mode/plugins/tts.py +286 -0
  293. narrative_ai/engines/voice_mode/security_hooks.py +277 -0
  294. narrative_ai/engines/voice_mode/stt_adapter.py +429 -0
  295. narrative_ai/engines/voice_mode/tts_adapter.py +325 -0
  296. narrative_ai/engines/voice_mode/voice_agent.py +171 -0
  297. narrative_ai/engines/voice_mode/webrtc_vad.py +337 -0
  298. narrative_ai/engines/voice_mode/worker.py +715 -0
  299. narrative_ai/engines/web_intel/__init__.py +17 -0
  300. narrative_ai/engines/web_intel/api.py +79 -0
  301. narrative_ai/engines/web_intel/cache.py +131 -0
  302. narrative_ai/engines/web_intel/engine.py +326 -0
  303. narrative_ai/engines/web_intel/models.py +74 -0
  304. narrative_ai/engines/web_intel/providers.py +178 -0
  305. narrative_ai/infrastructure/__init__.py +5 -0
  306. narrative_ai/infrastructure/cache/__init__.py +37 -0
  307. narrative_ai/infrastructure/cache/base_cache.py +30 -0
  308. narrative_ai/infrastructure/cache/config.py +41 -0
  309. narrative_ai/infrastructure/cache/memcached_cache.py +0 -0
  310. narrative_ai/infrastructure/cache/memory_cache.py +54 -0
  311. narrative_ai/infrastructure/cache/redis_cache.py +187 -0
  312. narrative_ai/infrastructure/cache/user_profile_cache.py +129 -0
  313. narrative_ai/infrastructure/config.py +30 -0
  314. narrative_ai/infrastructure/database/__init__.py +21 -0
  315. narrative_ai/infrastructure/database/config.py +57 -0
  316. narrative_ai/infrastructure/database/connection.py +86 -0
  317. narrative_ai/infrastructure/database/migrations/env.py +95 -0
  318. narrative_ai/infrastructure/database/migrations/versions/001_initial_schema.py +200 -0
  319. narrative_ai/infrastructure/database/migrations/versions/002_add_pgvector.py +76 -0
  320. narrative_ai/infrastructure/database/migrations/versions/003_add_indexes.py +67 -0
  321. narrative_ai/infrastructure/database/migrations/versions/004_audit_immutability.py +62 -0
  322. narrative_ai/infrastructure/database/migrations/versions/005_add_user_preferences.py +55 -0
  323. narrative_ai/infrastructure/database/migrations/versions/006_add_entry_media.py +39 -0
  324. narrative_ai/infrastructure/database/migrations/versions/007_audit_logs_tenant_timestamp_index.py +29 -0
  325. narrative_ai/infrastructure/database/models/__init__.py +41 -0
  326. narrative_ai/infrastructure/database/models/analytics_event_model.py +23 -0
  327. narrative_ai/infrastructure/database/models/audio_file_model.py +32 -0
  328. narrative_ai/infrastructure/database/models/audit_log_model.py +35 -0
  329. narrative_ai/infrastructure/database/models/base.py +24 -0
  330. narrative_ai/infrastructure/database/models/conversation_message_model.py +26 -0
  331. narrative_ai/infrastructure/database/models/conversation_model.py +29 -0
  332. narrative_ai/infrastructure/database/models/diary_embedding_model.py +34 -0
  333. narrative_ai/infrastructure/database/models/diary_entry_model.py +37 -0
  334. narrative_ai/infrastructure/database/models/entry_media_model.py +25 -0
  335. narrative_ai/infrastructure/database/models/export_job_model.py +22 -0
  336. narrative_ai/infrastructure/database/models/subscription_model.py +21 -0
  337. narrative_ai/infrastructure/database/models/user_model.py +28 -0
  338. narrative_ai/infrastructure/database/models/user_preference_model.py +29 -0
  339. narrative_ai/infrastructure/database/repositories/__init__.py +17 -0
  340. narrative_ai/infrastructure/database/repositories/analytics_event_repository_impl.py +40 -0
  341. narrative_ai/infrastructure/database/repositories/audio_repository_impl.py +98 -0
  342. narrative_ai/infrastructure/database/repositories/base_repository.py +64 -0
  343. narrative_ai/infrastructure/database/repositories/conversation_repository_impl.py +148 -0
  344. narrative_ai/infrastructure/database/repositories/entry_media_repository_impl.py +58 -0
  345. narrative_ai/infrastructure/database/repositories/entry_repository_impl.py +244 -0
  346. narrative_ai/infrastructure/database/repositories/search_repository_impl.py +121 -0
  347. narrative_ai/infrastructure/database/repositories/user_repository_impl.py +106 -0
  348. narrative_ai/infrastructure/database/schema.py +42 -0
  349. narrative_ai/infrastructure/database/session.py +64 -0
  350. narrative_ai/infrastructure/external/__init__.py +0 -0
  351. narrative_ai/infrastructure/external/email_client.py +0 -0
  352. narrative_ai/infrastructure/external/llm_client.py +0 -0
  353. narrative_ai/infrastructure/external/notification_client.py +0 -0
  354. narrative_ai/infrastructure/external/stt_client.py +0 -0
  355. narrative_ai/infrastructure/security/__init__.py +0 -0
  356. narrative_ai/infrastructure/security/auth_service.py +0 -0
  357. narrative_ai/infrastructure/security/config.py +0 -0
  358. narrative_ai/infrastructure/security/encryption.py +0 -0
  359. narrative_ai/infrastructure/security/jwt_service.py +113 -0
  360. narrative_ai/infrastructure/security/password_hasher.py +37 -0
  361. narrative_ai/infrastructure/security/rate_limiter.py +0 -0
  362. narrative_ai/infrastructure/storage/__init__.py +0 -0
  363. narrative_ai/infrastructure/storage/azure_storage.py +0 -0
  364. narrative_ai/infrastructure/storage/base_storage.py +0 -0
  365. narrative_ai/infrastructure/storage/config.py +0 -0
  366. narrative_ai/infrastructure/storage/gcs_storage.py +0 -0
  367. narrative_ai/infrastructure/storage/local_storage.py +0 -0
  368. narrative_ai/infrastructure/storage/s3_storage.py +0 -0
  369. narrative_ai/infrastructure/vector/__init__.py +31 -0
  370. narrative_ai/infrastructure/vector/base_vector_store.py +153 -0
  371. narrative_ai/infrastructure/vector/config.py +39 -0
  372. narrative_ai/infrastructure/vector/milvus_store.py +0 -0
  373. narrative_ai/infrastructure/vector/pgvector_store.py +120 -0
  374. narrative_ai/infrastructure/vector/pinecone_store.py +0 -0
  375. narrative_ai/infrastructure/vector/qdrant_store.py +424 -0
  376. narrative_ai/models/__init__.py +10 -0
  377. narrative_ai/models/registry.py +268 -0
  378. narrative_ai/security/__init__.py +67 -0
  379. narrative_ai/security/audit_trail.py +834 -0
  380. narrative_ai/security/circuit_breaker.py +179 -0
  381. narrative_ai/security/engine_integration.py +251 -0
  382. narrative_ai/security/error_handling.py +439 -0
  383. narrative_ai/security/input_validation.py +513 -0
  384. narrative_ai/security/rate_limiting.py +1069 -0
  385. narrative_ai/security/stream_isolation.py +389 -0
  386. narrative_ai/shared/__init__.py +0 -0
  387. narrative_ai/shared/constants.py +0 -0
  388. narrative_ai/shared/crypto.py +0 -0
  389. narrative_ai/shared/date_utils.py +0 -0
  390. narrative_ai/shared/decorators.py +0 -0
  391. narrative_ai/shared/exceptions.py +0 -0
  392. narrative_ai/shared/jwt_utils.py +0 -0
  393. narrative_ai/shared/logger.py +0 -0
  394. narrative_ai/shared/retry_utils.py +62 -0
  395. narrative_ai/shared/typing.py +0 -0
  396. narrative_ai/shared/validators.py +0 -0
  397. narrative_ai_framework-0.1.0.dist-info/METADATA +211 -0
  398. narrative_ai_framework-0.1.0.dist-info/RECORD +402 -0
  399. narrative_ai_framework-0.1.0.dist-info/WHEEL +5 -0
  400. narrative_ai_framework-0.1.0.dist-info/entry_points.txt +6 -0
  401. narrative_ai_framework-0.1.0.dist-info/licenses/LICENSE +21 -0
  402. narrative_ai_framework-0.1.0.dist-info/top_level.txt +2 -0
config/__init__.py ADDED
@@ -0,0 +1,31 @@
1
+ """
2
+ Configuration module for Narrative AI Framework.
3
+
4
+ This module provides centralized configuration management for all
5
+ providers and services.
6
+ """
7
+
8
+ from .providers import (
9
+ ProvidersConfig,
10
+ ProviderInfo,
11
+ ProviderType,
12
+ get_providers_config,
13
+ get_llm_provider,
14
+ get_stt_provider,
15
+ get_tts_provider,
16
+ get_web_provider,
17
+ )
18
+
19
+ __all__ = [
20
+ # Classes
21
+ "ProvidersConfig",
22
+ "ProviderInfo",
23
+ "ProviderType",
24
+
25
+ # Functions
26
+ "get_providers_config",
27
+ "get_llm_provider",
28
+ "get_stt_provider",
29
+ "get_tts_provider",
30
+ "get_web_provider",
31
+ ]
config/env.example.txt ADDED
@@ -0,0 +1,96 @@
1
+ # =============================================================================
2
+ # NARRATIVE AI - ENVIRONMENT VARIABLES
3
+ # =============================================================================
4
+ #
5
+ # Instructions:
6
+ # 1. Copy this file to .env in the project root (the directory that contains
7
+ # framework/ and config/). The API loads .env from the project root.
8
+ # 2. Fill in your API keys
9
+ # 3. The providers.yml file references these variables
10
+ #
11
+ # =============================================================================
12
+
13
+ # =============================================================================
14
+ # LLM PROVIDERS
15
+ # =============================================================================
16
+
17
+ # ------------- GEMINI (Google) - FREE -------------
18
+ # Get your key: https://aistudio.google.com/apikey
19
+ GEMINI_API_KEY=your_gemini_api_key_here
20
+
21
+ # ------------- OPENAI (GPT) - Paid -------------
22
+ # Get your key: https://platform.openai.com/api-keys
23
+ OPENAI_API_KEY=your_openai_api_key_here
24
+ OPENAI_ORG_ID= # Optional
25
+
26
+ # ------------- WEB INTELLIGENCE (Optional) -------------
27
+ # Default provider is DuckDuckGo (no key required)
28
+ WEB_INTEL_ENABLED=1
29
+ WEB_INTEL_MODE=auto # off | auto | force
30
+ # For Google Custom Search (optional)
31
+ # GOOGLE_SEARCH_API_KEY=your_google_search_api_key
32
+ # GOOGLE_SEARCH_CX=your_google_search_engine_id
33
+
34
+ # ------------- ANTHROPIC (Claude) - Paid -------------
35
+ # Get your key: https://console.anthropic.com/
36
+ ANTHROPIC_API_KEY=your_anthropic_api_key_here
37
+
38
+ # =============================================================================
39
+ # STT/TTS PROVIDERS
40
+ # =============================================================================
41
+
42
+ # ------------- ELEVENLABS - FREE tier: 10K chars/month -------------
43
+ # Get your key: https://elevenlabs.io/app/settings/api-keys
44
+ ELEVENLABS_API_KEY=your_elevenlabs_api_key_here
45
+ ELEVENLABS_VOICE_ID= # Optional: Custom voice ID
46
+
47
+ # ------------- GOOGLE CLOUD (Optional) -------------
48
+ # Download credentials JSON from Google Cloud Console
49
+ GOOGLE_APPLICATION_CREDENTIALS=/path/to/credentials.json
50
+
51
+ # =============================================================================
52
+ # REAL-TIME (LIVEKIT)
53
+ # =============================================================================
54
+
55
+ # ------------- LIVEKIT - FREE tier available -------------
56
+ # Get credentials: https://cloud.livekit.io
57
+ LIVEKIT_URL=wss://your-project.livekit.cloud
58
+ LIVEKIT_API_KEY=your_livekit_api_key
59
+ LIVEKIT_API_SECRET=your_livekit_api_secret
60
+
61
+ # =============================================================================
62
+ # CUSTOM/LOCAL MODELS
63
+ # =============================================================================
64
+
65
+ # Fine-tuned Whisper server
66
+ WHISPER_MODEL_PATH= # Local path or HuggingFace ID
67
+ WHISPER_SERVER_URL=http://localhost:8000 # If using server mode
68
+
69
+ # Fine-tuned Conformer model
70
+ CONFORMER_MODEL_PATH= # Local path or HuggingFace ID
71
+
72
+ # Custom TTS server
73
+ CUSTOM_TTS_SERVER_URL=http://localhost:8001
74
+
75
+ # =============================================================================
76
+ # INFRASTRUCTURE
77
+ # =============================================================================
78
+
79
+ # Database (PostgreSQL)
80
+ DATABASE_URL=postgresql://narrative:narrative_dev_password@localhost:5432/narrative_ai
81
+
82
+ # Redis (Caching & Rate Limiting)
83
+ REDIS_URL=redis://localhost:6379/0
84
+
85
+ # Environment
86
+ ENV=development # development, staging, production
87
+
88
+ # =============================================================================
89
+ # SECURITY
90
+ # =============================================================================
91
+
92
+ # JWT Secret (generate with: openssl rand -hex 32)
93
+ JWT_SECRET=your_jwt_secret_here
94
+
95
+ # Encryption Key (generate with: python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())")
96
+ ENCRYPTION_KEY=your_encryption_key_here
config/providers.py ADDED
@@ -0,0 +1,454 @@
1
+ """
2
+ Providers Configuration Loader.
3
+
4
+ Loads and manages provider configurations from providers.yml.
5
+ Supports environment variable substitution and runtime toggling.
6
+ """
7
+
8
+ import os
9
+ import yaml
10
+ from pathlib import Path
11
+ from typing import Dict, Any, Optional, List
12
+ from dataclasses import dataclass
13
+ from enum import Enum
14
+
15
+
16
+ class ProviderType(str, Enum):
17
+ """Types of providers."""
18
+ LLM = "llm"
19
+ STT = "stt"
20
+ TTS = "tts"
21
+ REALTIME = "realtime"
22
+ EMBEDDINGS = "embeddings"
23
+ WEB = "web"
24
+
25
+
26
+ @dataclass
27
+ class ProviderInfo:
28
+ """Information about a single provider."""
29
+ name: str
30
+ type: ProviderType
31
+ enabled: bool
32
+ primary: bool
33
+ config: Dict[str, Any]
34
+
35
+ @property
36
+ def api_key(self) -> Optional[str]:
37
+ """Get API key from environment (stripped of whitespace/BOM)."""
38
+ key_env = self.config.get("api_key_env")
39
+ if key_env:
40
+ raw = os.getenv(key_env) or ""
41
+ raw = raw.strip().strip("\ufeff")
42
+ return raw or None
43
+ return None
44
+
45
+ @property
46
+ def is_available(self) -> bool:
47
+ """Check if provider is available (enabled + has credentials)."""
48
+ if not self.enabled:
49
+ return False
50
+
51
+ # Check if API key is required and present
52
+ key_env = self.config.get("api_key_env")
53
+ if key_env:
54
+ return bool(os.getenv(key_env))
55
+
56
+ return True
57
+
58
+
59
+ class ProvidersConfig:
60
+ """
61
+ Providers Configuration Manager.
62
+
63
+ Loads configuration from providers.yml and provides easy access
64
+ to provider settings with environment variable substitution.
65
+
66
+ Example:
67
+ config = ProvidersConfig()
68
+
69
+ # Get primary LLM provider
70
+ llm = config.get_primary_provider(ProviderType.LLM)
71
+ print(f"Using LLM: {llm.name}")
72
+
73
+ # Get all enabled STT providers
74
+ stt_providers = config.get_enabled_providers(ProviderType.STT)
75
+
76
+ # Toggle a provider
77
+ config.set_enabled("openai", ProviderType.LLM, False)
78
+ """
79
+
80
+ DEFAULT_CONFIG_PATH = Path(__file__).parent / "providers.yml"
81
+
82
+ def __init__(self, config_path: Optional[Path] = None):
83
+ """
84
+ Initialize providers configuration.
85
+
86
+ Args:
87
+ config_path: Path to providers.yml (uses default if not provided)
88
+ """
89
+ self.config_path = config_path or self.DEFAULT_CONFIG_PATH
90
+ self._config: Dict[str, Any] = {}
91
+ self._providers: Dict[str, Dict[str, ProviderInfo]] = {}
92
+
93
+ self._load_config()
94
+
95
+ def _load_config(self) -> None:
96
+ """Load configuration from YAML file."""
97
+ if not self.config_path.exists():
98
+ raise FileNotFoundError(f"Providers config not found: {self.config_path}")
99
+
100
+ with open(self.config_path, 'r', encoding='utf-8') as f:
101
+ self._config = yaml.safe_load(f)
102
+
103
+ # Parse providers
104
+ self._parse_providers()
105
+
106
+ def _parse_providers(self) -> None:
107
+ """Parse provider configurations into ProviderInfo objects."""
108
+ self._providers = {}
109
+ non_provider_keys = {
110
+ "enabled",
111
+ "mode",
112
+ "primary_provider",
113
+ "fallback_order",
114
+ "min_confidence",
115
+ "vad",
116
+ "emotion_detection",
117
+ "default_provider",
118
+ "default_dimension",
119
+ "timeout_ms",
120
+ "max_results",
121
+ "freshness_ttl",
122
+ "deny_domains",
123
+ }
124
+
125
+ for provider_type in ProviderType:
126
+ type_key = provider_type.value
127
+ if type_key not in self._config:
128
+ continue
129
+
130
+ type_config = self._config[type_key]
131
+ self._providers[type_key] = {}
132
+
133
+ for name, config in type_config.items():
134
+ # Skip non-provider keys
135
+ if name in non_provider_keys:
136
+ continue
137
+
138
+ if isinstance(config, dict):
139
+ self._providers[type_key][name] = ProviderInfo(
140
+ name=name,
141
+ type=provider_type,
142
+ enabled=config.get('enabled', False),
143
+ primary=config.get('primary', False),
144
+ config=config,
145
+ )
146
+
147
+ def reload(self) -> None:
148
+ """Reload configuration from file."""
149
+ self._load_config()
150
+
151
+ # =========================================================================
152
+ # Provider Access Methods
153
+ # =========================================================================
154
+
155
+ def get_provider(self, name: str, provider_type: ProviderType) -> Optional[ProviderInfo]:
156
+ """
157
+ Get a specific provider.
158
+
159
+ Args:
160
+ name: Provider name (e.g., "openai", "elevenlabs")
161
+ provider_type: Type of provider
162
+
163
+ Returns:
164
+ ProviderInfo or None if not found
165
+ """
166
+ type_providers = self._providers.get(provider_type.value, {})
167
+ return type_providers.get(name)
168
+
169
+ def get_all_providers(self, provider_type: ProviderType) -> List[ProviderInfo]:
170
+ """
171
+ Get all providers of a type.
172
+
173
+ Args:
174
+ provider_type: Type of provider
175
+
176
+ Returns:
177
+ List of ProviderInfo objects
178
+ """
179
+ type_providers = self._providers.get(provider_type.value, {})
180
+ return list(type_providers.values())
181
+
182
+ def get_enabled_providers(self, provider_type: ProviderType) -> List[ProviderInfo]:
183
+ """
184
+ Get all enabled providers of a type.
185
+
186
+ Args:
187
+ provider_type: Type of provider
188
+
189
+ Returns:
190
+ List of enabled ProviderInfo objects
191
+ """
192
+ return [p for p in self.get_all_providers(provider_type) if p.enabled]
193
+
194
+ def get_available_providers(self, provider_type: ProviderType) -> List[ProviderInfo]:
195
+ """
196
+ Get all available providers (enabled + has credentials).
197
+
198
+ Args:
199
+ provider_type: Type of provider
200
+
201
+ Returns:
202
+ List of available ProviderInfo objects
203
+ """
204
+ return [p for p in self.get_all_providers(provider_type) if p.is_available]
205
+
206
+ def get_primary_provider(self, provider_type: ProviderType) -> Optional[ProviderInfo]:
207
+ """
208
+ Get the primary provider for a type.
209
+
210
+ Args:
211
+ provider_type: Type of provider
212
+
213
+ Returns:
214
+ Primary ProviderInfo or None
215
+ """
216
+ for provider in self.get_all_providers(provider_type):
217
+ if provider.primary and provider.enabled:
218
+ return provider
219
+
220
+ # Fallback to first enabled
221
+ enabled = self.get_enabled_providers(provider_type)
222
+ return enabled[0] if enabled else None
223
+
224
+ def get_fallback_order(self, provider_type: ProviderType) -> List[str]:
225
+ """
226
+ Get fallback order for a provider type.
227
+
228
+ Args:
229
+ provider_type: Type of provider
230
+
231
+ Returns:
232
+ List of provider names in fallback order
233
+ """
234
+ type_config = self._config.get(provider_type.value, {})
235
+ return type_config.get('fallback_order', [])
236
+
237
+ def get_providers_in_order(self, provider_type: ProviderType) -> List[ProviderInfo]:
238
+ """
239
+ Get enabled providers in fallback order.
240
+
241
+ Args:
242
+ provider_type: Type of provider
243
+
244
+ Returns:
245
+ List of ProviderInfo in order
246
+ """
247
+ fallback_order = self.get_fallback_order(provider_type)
248
+ enabled = {p.name: p for p in self.get_enabled_providers(provider_type)}
249
+
250
+ result = []
251
+
252
+ # Add in fallback order
253
+ for name in fallback_order:
254
+ if name in enabled:
255
+ result.append(enabled.pop(name))
256
+
257
+ # Add any remaining
258
+ result.extend(enabled.values())
259
+
260
+ return result
261
+
262
+ # =========================================================================
263
+ # Provider Configuration Methods
264
+ # =========================================================================
265
+
266
+ def set_enabled(self, name: str, provider_type: ProviderType, enabled: bool) -> None:
267
+ """
268
+ Enable or disable a provider at runtime.
269
+
270
+ Note: This doesn't persist to file.
271
+
272
+ Args:
273
+ name: Provider name
274
+ provider_type: Type of provider
275
+ enabled: Whether to enable
276
+ """
277
+ provider = self.get_provider(name, provider_type)
278
+ if provider:
279
+ provider.enabled = enabled
280
+
281
+ def set_primary(self, name: str, provider_type: ProviderType) -> None:
282
+ """
283
+ Set a provider as primary.
284
+
285
+ Note: This doesn't persist to file.
286
+
287
+ Args:
288
+ name: Provider name
289
+ provider_type: Type of provider
290
+ """
291
+ # Clear existing primary
292
+ for provider in self.get_all_providers(provider_type):
293
+ provider.primary = False
294
+
295
+ # Set new primary
296
+ provider = self.get_provider(name, provider_type)
297
+ if provider:
298
+ provider.primary = True
299
+
300
+ def get_config(self, name: str, provider_type: ProviderType, key: str, default: Any = None) -> Any:
301
+ """
302
+ Get a specific configuration value for a provider.
303
+
304
+ Args:
305
+ name: Provider name
306
+ provider_type: Type of provider
307
+ key: Configuration key (supports dot notation: "models.default")
308
+ default: Default value if not found
309
+
310
+ Returns:
311
+ Configuration value
312
+ """
313
+ provider = self.get_provider(name, provider_type)
314
+ if not provider:
315
+ return default
316
+
317
+ # Support dot notation
318
+ config = provider.config
319
+ for k in key.split('.'):
320
+ if isinstance(config, dict) and k in config:
321
+ config = config[k]
322
+ else:
323
+ return default
324
+
325
+ return config
326
+
327
+ # =========================================================================
328
+ # Global Settings
329
+ # =========================================================================
330
+
331
+ @property
332
+ def global_config(self) -> Dict[str, Any]:
333
+ """Get global configuration."""
334
+ return self._config.get('global', {})
335
+
336
+ @property
337
+ def default_timeout(self) -> int:
338
+ """Get default timeout."""
339
+ return self.global_config.get('default_timeout', 30)
340
+
341
+ @property
342
+ def retry_config(self) -> Dict[str, Any]:
343
+ """Get retry configuration."""
344
+ return self.global_config.get('retry', {
345
+ 'max_attempts': 3,
346
+ 'backoff_multiplier': 2.0,
347
+ 'initial_delay': 1.0,
348
+ })
349
+
350
+ @property
351
+ def monitoring_config(self) -> Dict[str, Any]:
352
+ """Get monitoring configuration."""
353
+ return self._config.get('monitoring', {})
354
+
355
+ # =========================================================================
356
+ # Utility Methods
357
+ # =========================================================================
358
+
359
+ def get_api_key(self, name: str, provider_type: ProviderType) -> Optional[str]:
360
+ """
361
+ Get API key for a provider.
362
+
363
+ Args:
364
+ name: Provider name
365
+ provider_type: Type of provider
366
+
367
+ Returns:
368
+ API key from environment or None
369
+ """
370
+ provider = self.get_provider(name, provider_type)
371
+ return provider.api_key if provider else None
372
+
373
+ def to_dict(self) -> Dict[str, Any]:
374
+ """Export current configuration as dictionary."""
375
+ return self._config.copy()
376
+
377
+ def __repr__(self) -> str:
378
+ provider_counts = {
379
+ ptype.value: len(self.get_enabled_providers(ptype))
380
+ for ptype in ProviderType
381
+ }
382
+ return f"ProvidersConfig({provider_counts})"
383
+
384
+
385
+ # =============================================================================
386
+ # Convenience Functions
387
+ # =============================================================================
388
+
389
+ _global_config: Optional[ProvidersConfig] = None
390
+
391
+
392
+ def get_providers_config() -> ProvidersConfig:
393
+ """
394
+ Get the global providers configuration.
395
+
396
+ Returns:
397
+ ProvidersConfig instance
398
+ """
399
+ global _global_config
400
+ if _global_config is None:
401
+ _global_config = ProvidersConfig()
402
+ return _global_config
403
+
404
+
405
+ def get_llm_provider(name: Optional[str] = None) -> Optional[ProviderInfo]:
406
+ """Get an LLM provider (primary if name not specified)."""
407
+ config = get_providers_config()
408
+ if name:
409
+ return config.get_provider(name, ProviderType.LLM)
410
+ return config.get_primary_provider(ProviderType.LLM)
411
+
412
+
413
+ def get_stt_provider(name: Optional[str] = None) -> Optional[ProviderInfo]:
414
+ """Get an STT provider (primary if name not specified)."""
415
+ config = get_providers_config()
416
+ if name:
417
+ return config.get_provider(name, ProviderType.STT)
418
+ return config.get_primary_provider(ProviderType.STT)
419
+
420
+
421
+ def get_tts_provider(name: Optional[str] = None) -> Optional[ProviderInfo]:
422
+ """Get a TTS provider (primary if name not specified)."""
423
+ config = get_providers_config()
424
+ if name:
425
+ return config.get_provider(name, ProviderType.TTS)
426
+ return config.get_primary_provider(ProviderType.TTS)
427
+
428
+
429
+ def get_web_provider(name: Optional[str] = None) -> Optional[ProviderInfo]:
430
+ """Get a Web provider (primary if name not specified)."""
431
+ config = get_providers_config()
432
+ if name:
433
+ return config.get_provider(name, ProviderType.WEB)
434
+ return config.get_primary_provider(ProviderType.WEB)
435
+
436
+
437
+ if __name__ == "__main__":
438
+ # Test the configuration
439
+ config = ProvidersConfig()
440
+
441
+ print("=== Providers Configuration ===\n")
442
+
443
+ for ptype in ProviderType:
444
+ print(f"\n{ptype.value.upper()} Providers:")
445
+ print("-" * 40)
446
+
447
+ for provider in config.get_all_providers(ptype):
448
+ status = "✓" if provider.is_available else "○" if provider.enabled else "✗"
449
+ primary = " [PRIMARY]" if provider.primary else ""
450
+ print(f" {status} {provider.name}{primary}")
451
+
452
+ primary = config.get_primary_provider(ptype)
453
+ if primary:
454
+ print(f" → Primary: {primary.name}")