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.
- config/__init__.py +31 -0
- config/env.example.txt +96 -0
- config/providers.py +454 -0
- config/providers.yml +621 -0
- narrative_ai/__init__.py +28 -0
- narrative_ai/api/__init__.py +0 -0
- narrative_ai/api/__main__.py +6 -0
- narrative_ai/api/app.py +415 -0
- narrative_ai/api/dependencies.py +484 -0
- narrative_ai/api/http_helpers.py +26 -0
- narrative_ai/api/idempotency.py +89 -0
- narrative_ai/api/middleware/__init__.py +0 -0
- narrative_ai/api/middleware/auth.py +129 -0
- narrative_ai/api/middleware/correlation_id.py +60 -0
- narrative_ai/api/middleware/error_handler.py +196 -0
- narrative_ai/api/middleware/jwt_user_id.py +49 -0
- narrative_ai/api/middleware/logging.py +51 -0
- narrative_ai/api/middleware/rate_limit.py +163 -0
- narrative_ai/api/middleware/security_headers.py +30 -0
- narrative_ai/api/middleware/tenant.py +1 -0
- narrative_ai/api/models/__init__.py +0 -0
- narrative_ai/api/models/audio.py +0 -0
- narrative_ai/api/models/companion.py +0 -0
- narrative_ai/api/models/entry.py +0 -0
- narrative_ai/api/models/error.py +0 -0
- narrative_ai/api/models/pagination.py +0 -0
- narrative_ai/api/models/search.py +0 -0
- narrative_ai/api/routes/__init__.py +1 -0
- narrative_ai/api/routes/analytics.py +137 -0
- narrative_ai/api/routes/auth.py +444 -0
- narrative_ai/api/routes/companion.py +209 -0
- narrative_ai/api/routes/entries.py +658 -0
- narrative_ai/api/routes/export.py +21 -0
- narrative_ai/api/routes/ingestion.py +170 -0
- narrative_ai/api/routes/llm.py +408 -0
- narrative_ai/api/routes/ocr.py +89 -0
- narrative_ai/api/routes/rag.py +207 -0
- narrative_ai/api/routes/search.py +20 -0
- narrative_ai/api/routes/stt.py +274 -0
- narrative_ai/api/routes/tts.py +324 -0
- narrative_ai/api/routes/vlm.py +155 -0
- narrative_ai/api/routes/web_intel.py +79 -0
- narrative_ai/api/services/__init__.py +1 -0
- narrative_ai/api/services/entry_processor.py +211 -0
- narrative_ai/application/__init__.py +6 -0
- narrative_ai/application/dto/__init__.py +35 -0
- narrative_ai/application/dto/ingestion_dto.py +52 -0
- narrative_ai/application/dto/llm_dto.py +83 -0
- narrative_ai/application/dto/rag_dto.py +101 -0
- narrative_ai/application/dto/stt_dto.py +56 -0
- narrative_ai/application/dto/tts_dto.py +61 -0
- narrative_ai/application/dto/vlm_dto.py +64 -0
- narrative_ai/application/services/__init__.py +26 -0
- narrative_ai/application/services/companion_chat_pipeline.py +988 -0
- narrative_ai/application/services/entry_service.py +475 -0
- narrative_ai/application/services/ingestion_service.py +50 -0
- narrative_ai/application/services/llm_service.py +79 -0
- narrative_ai/application/services/rag_service.py +94 -0
- narrative_ai/application/services/stt_service.py +40 -0
- narrative_ai/application/services/tts_service.py +59 -0
- narrative_ai/application/services/user_profile_service.py +109 -0
- narrative_ai/application/services/user_service.py +146 -0
- narrative_ai/application/services/vlm_service.py +54 -0
- narrative_ai/domain/__init__.py +6 -0
- narrative_ai/domain/domain_services/__init__.py +17 -0
- narrative_ai/domain/domain_services/ingestion_engine.py +36 -0
- narrative_ai/domain/domain_services/llm_engine.py +39 -0
- narrative_ai/domain/domain_services/rag_service.py +41 -0
- narrative_ai/domain/domain_services/stt_engine.py +34 -0
- narrative_ai/domain/domain_services/tts_engine.py +39 -0
- narrative_ai/domain/domain_services/vlm_engine.py +24 -0
- narrative_ai/domain/entities/__init__.py +21 -0
- narrative_ai/domain/entities/analytics.py +24 -0
- narrative_ai/domain/entities/audio_file.py +48 -0
- narrative_ai/domain/entities/conversation.py +95 -0
- narrative_ai/domain/entities/diary_entry.py +77 -0
- narrative_ai/domain/entities/user.py +63 -0
- narrative_ai/domain/events/__init__.py +15 -0
- narrative_ai/domain/events/chat_events.py +0 -0
- narrative_ai/domain/events/domain_event.py +17 -0
- narrative_ai/domain/events/entry_events.py +57 -0
- narrative_ai/domain/events/event_bus.py +67 -0
- narrative_ai/domain/events/export_events.py +0 -0
- narrative_ai/domain/events/search_events.py +0 -0
- narrative_ai/domain/exceptions/__init__.py +31 -0
- narrative_ai/domain/exceptions/chat_exceptions.py +0 -0
- narrative_ai/domain/exceptions/domain_exception.py +33 -0
- narrative_ai/domain/exceptions/entry_exceptions.py +44 -0
- narrative_ai/domain/exceptions/export_exceptions.py +0 -0
- narrative_ai/domain/exceptions/search_exceptions.py +0 -0
- narrative_ai/domain/exceptions/user_exceptions.py +37 -0
- narrative_ai/domain/repositories/__init__.py +20 -0
- narrative_ai/domain/repositories/audio_repository.py +39 -0
- narrative_ai/domain/repositories/conversation_repository.py +46 -0
- narrative_ai/domain/repositories/entry_repository.py +58 -0
- narrative_ai/domain/repositories/search_repository.py +47 -0
- narrative_ai/domain/repositories/user_repository.py +45 -0
- narrative_ai/domain/value_objects/__init__.py +17 -0
- narrative_ai/domain/value_objects/audio_metadata.py +19 -0
- narrative_ai/domain/value_objects/embedding.py +27 -0
- narrative_ai/domain/value_objects/emotion.py +28 -0
- narrative_ai/domain/value_objects/entry_id.py +32 -0
- narrative_ai/domain/value_objects/tenant_id.py +51 -0
- narrative_ai/domain/value_objects/user_id.py +32 -0
- narrative_ai/engines/__init__.py +95 -0
- narrative_ai/engines/analytics/__init__.py +0 -0
- narrative_ai/engines/analytics/config.py +0 -0
- narrative_ai/engines/analytics/emotion_analytics.py +0 -0
- narrative_ai/engines/analytics/metrics_calculator.py +0 -0
- narrative_ai/engines/analytics/pattern_detector.py +0 -0
- narrative_ai/engines/companion/__init__.py +0 -0
- narrative_ai/engines/companion/audio_response_builder.py +11 -0
- narrative_ai/engines/companion/companion_engine.py +0 -0
- narrative_ai/engines/companion/config.py +0 -0
- narrative_ai/engines/companion/conversation_manager.py +0 -0
- narrative_ai/engines/companion/intent_classifier.py +381 -0
- narrative_ai/engines/companion/intent_exemplars.yaml +89 -0
- narrative_ai/engines/companion/llm_client.py +0 -0
- narrative_ai/engines/companion/prompt_builder.py +0 -0
- narrative_ai/engines/companion/response_processor.py +0 -0
- narrative_ai/engines/embeddings/__init__.py +0 -0
- narrative_ai/engines/embeddings/batch_processor.py +0 -0
- narrative_ai/engines/embeddings/chunking.py +0 -0
- narrative_ai/engines/embeddings/config.py +0 -0
- narrative_ai/engines/embeddings/embedding_engine.py +0 -0
- narrative_ai/engines/embeddings/model_loader.py +0 -0
- narrative_ai/engines/export/__init__.py +0 -0
- narrative_ai/engines/export/audiobook_generator.py +0 -0
- narrative_ai/engines/export/config.py +0 -0
- narrative_ai/engines/export/epub_generator.py +0 -0
- narrative_ai/engines/export/export_engine.py +0 -0
- narrative_ai/engines/export/pdf_generator.py +0 -0
- narrative_ai/engines/input_processor/__init__.py +130 -0
- narrative_ai/engines/input_processor/api.py +169 -0
- narrative_ai/engines/input_processor/assembler.py +334 -0
- narrative_ai/engines/input_processor/audio_processor.py +339 -0
- narrative_ai/engines/input_processor/config.py +476 -0
- narrative_ai/engines/input_processor/document_extractor.py +424 -0
- narrative_ai/engines/input_processor/factory.py +156 -0
- narrative_ai/engines/input_processor/image_processor.py +280 -0
- narrative_ai/engines/input_processor/ingestion_config.yaml +143 -0
- narrative_ai/engines/input_processor/ingestion_pipeline.py +516 -0
- narrative_ai/engines/input_processor/input_handler.py +414 -0
- narrative_ai/engines/input_processor/ocr_processor.py +91 -0
- narrative_ai/engines/input_processor/stt_processor.py +119 -0
- narrative_ai/engines/input_processor/types.py +436 -0
- narrative_ai/engines/llm/__init__.py +253 -0
- narrative_ai/engines/llm/api.py +230 -0
- narrative_ai/engines/llm/base_llm.py +423 -0
- narrative_ai/engines/llm/claude_llm.py +674 -0
- narrative_ai/engines/llm/config.py +666 -0
- narrative_ai/engines/llm/conversation_manager.py +620 -0
- narrative_ai/engines/llm/conversation_storage.py +853 -0
- narrative_ai/engines/llm/deepseek_llm.py +687 -0
- narrative_ai/engines/llm/error_mapper.py +627 -0
- narrative_ai/engines/llm/gemini_llm.py +867 -0
- narrative_ai/engines/llm/input_processor.py +555 -0
- narrative_ai/engines/llm/llm_engine.py +1212 -0
- narrative_ai/engines/llm/llm_strategy.py +705 -0
- narrative_ai/engines/llm/ollama_llm.py +386 -0
- narrative_ai/engines/llm/openai_llm.py +773 -0
- narrative_ai/engines/llm/response_processor.py +535 -0
- narrative_ai/engines/llm/token_budget.py +116 -0
- narrative_ai/engines/llm/xai_llm.py +650 -0
- narrative_ai/engines/ocr/__init__.py +52 -0
- narrative_ai/engines/ocr/api.py +318 -0
- narrative_ai/engines/ocr/config/config.yaml +41 -0
- narrative_ai/engines/ocr/config.py +0 -0
- narrative_ai/engines/ocr/core/__init__.py +14 -0
- narrative_ai/engines/ocr/core/api.py +59 -0
- narrative_ai/engines/ocr/core/factory.py +20 -0
- narrative_ai/engines/ocr/core/registry.py +46 -0
- narrative_ai/engines/ocr/demos/streamlit_app.py +222 -0
- narrative_ai/engines/ocr/dewarpnet/__init__.py +5 -0
- narrative_ai/engines/ocr/dewarpnet/infer.py +133 -0
- narrative_ai/engines/ocr/dewarpnet/models/densenetccnl.py +176 -0
- narrative_ai/engines/ocr/dewarpnet/models/unetnc.py +82 -0
- narrative_ai/engines/ocr/dewarpnet/utils.py +12 -0
- narrative_ai/engines/ocr/filters/__init__.py +18 -0
- narrative_ai/engines/ocr/filters/base.py +108 -0
- narrative_ai/engines/ocr/filters/dewarpnet_filter.py +218 -0
- narrative_ai/engines/ocr/filters/dewarpnet_models/__init__.py +29 -0
- narrative_ai/engines/ocr/filters/dewarpnet_models/densenetccnl.py +169 -0
- narrative_ai/engines/ocr/filters/dewarpnet_models/unetnc.py +88 -0
- narrative_ai/engines/ocr/filters/docres_filter.py +295 -0
- narrative_ai/engines/ocr/filters/remote_docres.py +259 -0
- narrative_ai/engines/ocr/kaggle_server.py +666 -0
- narrative_ai/engines/ocr/pipeline.py +428 -0
- narrative_ai/engines/ocr/providers/__init__.py +3 -0
- narrative_ai/engines/ocr/providers/remote.py +218 -0
- narrative_ai/engines/ocr/requirements.txt +39 -0
- narrative_ai/engines/ocr/tests/__init__.py +1 -0
- narrative_ai/engines/ocr/tests/debug_server.py +53 -0
- narrative_ai/engines/ocr/tests/test_filters.py +22 -0
- narrative_ai/engines/ocr/tests/test_functional_api.py +59 -0
- narrative_ai/engines/ocr/tests/test_pipeline.py +43 -0
- narrative_ai/engines/ocr/tests/test_providers.py +34 -0
- narrative_ai/engines/ocr/tests/test_utils.py +39 -0
- narrative_ai/engines/ocr/tests/verify_integration.py +60 -0
- narrative_ai/engines/ocr/tests/verify_universal_formats.py +51 -0
- narrative_ai/engines/ocr/utils/docres_utils.py +109 -0
- narrative_ai/engines/ocr/utils/image.py +29 -0
- narrative_ai/engines/ocr/utils/image_processing.py +69 -0
- narrative_ai/engines/rag/__init__.py +49 -0
- narrative_ai/engines/rag/api.py +295 -0
- narrative_ai/engines/rag/augmentation.py +175 -0
- narrative_ai/engines/rag/cache.py +441 -0
- narrative_ai/engines/rag/config.py +185 -0
- narrative_ai/engines/rag/context_builder.py +195 -0
- narrative_ai/engines/rag/hybrid_search.py +203 -0
- narrative_ai/engines/rag/memory_config.yaml +64 -0
- narrative_ai/engines/rag/memory_manager.py +805 -0
- narrative_ai/engines/rag/providers/__init__.py +78 -0
- narrative_ai/engines/rag/providers/base.py +163 -0
- narrative_ai/engines/rag/providers/embeddings/__init__.py +14 -0
- narrative_ai/engines/rag/providers/embeddings/bge_m3.py +226 -0
- narrative_ai/engines/rag/providers/embeddings/cohere_provider.py +103 -0
- narrative_ai/engines/rag/providers/embeddings/openai_provider.py +90 -0
- narrative_ai/engines/rag/providers/rerankers/__init__.py +11 -0
- narrative_ai/engines/rag/providers/rerankers/bge_reranker.py +66 -0
- narrative_ai/engines/rag/providers/rerankers/cohere_reranker.py +81 -0
- narrative_ai/engines/rag/query_processor.py +328 -0
- narrative_ai/engines/rag/ranking.py +0 -0
- narrative_ai/engines/rag/retrieval_engine.py +0 -0
- narrative_ai/engines/rag/strategies/__init__.py +33 -0
- narrative_ai/engines/rag/strategies/base.py +97 -0
- narrative_ai/engines/rag/strategies/default.py +75 -0
- narrative_ai/engines/rag/strategies/diary.py +111 -0
- narrative_ai/engines/rag/text_splitter.py +389 -0
- narrative_ai/engines/rag/types.py +104 -0
- narrative_ai/engines/storage_interface.py +130 -0
- narrative_ai/engines/stt/__init__.py +251 -0
- narrative_ai/engines/stt/__main__.py +6 -0
- narrative_ai/engines/stt/api.py +266 -0
- narrative_ai/engines/stt/audio_processor.py +612 -0
- narrative_ai/engines/stt/base_stt.py +441 -0
- narrative_ai/engines/stt/config.py +663 -0
- narrative_ai/engines/stt/conformer_model.py +0 -0
- narrative_ai/engines/stt/elevenlabs_stt.py +1179 -0
- narrative_ai/engines/stt/emotion_detector.py +717 -0
- narrative_ai/engines/stt/faster_whisper_stt.py +408 -0
- narrative_ai/engines/stt/main.py +98 -0
- narrative_ai/engines/stt/stt_engine.py +1765 -0
- narrative_ai/engines/stt/stt_strategy.py +766 -0
- narrative_ai/engines/stt/vad_processor.py +579 -0
- narrative_ai/engines/stt/whisper_stt.py +706 -0
- narrative_ai/engines/text_enhancement/__init__.py +0 -0
- narrative_ai/engines/text_enhancement/config.py +0 -0
- narrative_ai/engines/text_enhancement/grammar_fixer.py +0 -0
- narrative_ai/engines/text_enhancement/llm_enhancer.py +0 -0
- narrative_ai/engines/text_enhancement/normalizer.py +0 -0
- narrative_ai/engines/text_enhancement/validator.py +0 -0
- narrative_ai/engines/tts/__init__.py +254 -0
- narrative_ai/engines/tts/__main__.py +6 -0
- narrative_ai/engines/tts/api.py +194 -0
- narrative_ai/engines/tts/audio_processor.py +640 -0
- narrative_ai/engines/tts/base_tts.py +740 -0
- narrative_ai/engines/tts/config.py +599 -0
- narrative_ai/engines/tts/elevenlabs_tts.py +871 -0
- narrative_ai/engines/tts/emotion_applicator.py +464 -0
- narrative_ai/engines/tts/main.py +113 -0
- narrative_ai/engines/tts/model_loader.py +0 -0
- narrative_ai/engines/tts/openai_tts.py +712 -0
- narrative_ai/engines/tts/prosody_engine.py +0 -0
- narrative_ai/engines/tts/streaming.py +0 -0
- narrative_ai/engines/tts/tts_engine.py +1140 -0
- narrative_ai/engines/tts/tts_strategy.py +755 -0
- narrative_ai/engines/tts/voice_selector.py +0 -0
- narrative_ai/engines/vlm/__init__.py +5 -0
- narrative_ai/engines/vlm/api.py +106 -0
- narrative_ai/engines/vlm/base.py +49 -0
- narrative_ai/engines/vlm/config.py +70 -0
- narrative_ai/engines/vlm/factory.py +49 -0
- narrative_ai/engines/vlm/processor.py +306 -0
- narrative_ai/engines/vlm/providers/google.py +121 -0
- narrative_ai/engines/vlm/providers/ollama_cloud.py +112 -0
- narrative_ai/engines/vlm/providers/ollama_local.py +90 -0
- narrative_ai/engines/vlm/providers/openai.py +97 -0
- narrative_ai/engines/vlm/types.py +38 -0
- narrative_ai/engines/vlm/utils.py +44 -0
- narrative_ai/engines/voice_mode/__init__.py +36 -0
- narrative_ai/engines/voice_mode/agent.py +244 -0
- narrative_ai/engines/voice_mode/api.py +42 -0
- narrative_ai/engines/voice_mode/config.py +177 -0
- narrative_ai/engines/voice_mode/constants.py +22 -0
- narrative_ai/engines/voice_mode/conversation.py +419 -0
- narrative_ai/engines/voice_mode/llm_adapter.py +348 -0
- narrative_ai/engines/voice_mode/main.py +37 -0
- narrative_ai/engines/voice_mode/plugins/__init__.py +23 -0
- narrative_ai/engines/voice_mode/plugins/llm.py +288 -0
- narrative_ai/engines/voice_mode/plugins/stt.py +485 -0
- narrative_ai/engines/voice_mode/plugins/tts.py +286 -0
- narrative_ai/engines/voice_mode/security_hooks.py +277 -0
- narrative_ai/engines/voice_mode/stt_adapter.py +429 -0
- narrative_ai/engines/voice_mode/tts_adapter.py +325 -0
- narrative_ai/engines/voice_mode/voice_agent.py +171 -0
- narrative_ai/engines/voice_mode/webrtc_vad.py +337 -0
- narrative_ai/engines/voice_mode/worker.py +715 -0
- narrative_ai/engines/web_intel/__init__.py +17 -0
- narrative_ai/engines/web_intel/api.py +79 -0
- narrative_ai/engines/web_intel/cache.py +131 -0
- narrative_ai/engines/web_intel/engine.py +326 -0
- narrative_ai/engines/web_intel/models.py +74 -0
- narrative_ai/engines/web_intel/providers.py +178 -0
- narrative_ai/infrastructure/__init__.py +5 -0
- narrative_ai/infrastructure/cache/__init__.py +37 -0
- narrative_ai/infrastructure/cache/base_cache.py +30 -0
- narrative_ai/infrastructure/cache/config.py +41 -0
- narrative_ai/infrastructure/cache/memcached_cache.py +0 -0
- narrative_ai/infrastructure/cache/memory_cache.py +54 -0
- narrative_ai/infrastructure/cache/redis_cache.py +187 -0
- narrative_ai/infrastructure/cache/user_profile_cache.py +129 -0
- narrative_ai/infrastructure/config.py +30 -0
- narrative_ai/infrastructure/database/__init__.py +21 -0
- narrative_ai/infrastructure/database/config.py +57 -0
- narrative_ai/infrastructure/database/connection.py +86 -0
- narrative_ai/infrastructure/database/migrations/env.py +95 -0
- narrative_ai/infrastructure/database/migrations/versions/001_initial_schema.py +200 -0
- narrative_ai/infrastructure/database/migrations/versions/002_add_pgvector.py +76 -0
- narrative_ai/infrastructure/database/migrations/versions/003_add_indexes.py +67 -0
- narrative_ai/infrastructure/database/migrations/versions/004_audit_immutability.py +62 -0
- narrative_ai/infrastructure/database/migrations/versions/005_add_user_preferences.py +55 -0
- narrative_ai/infrastructure/database/migrations/versions/006_add_entry_media.py +39 -0
- narrative_ai/infrastructure/database/migrations/versions/007_audit_logs_tenant_timestamp_index.py +29 -0
- narrative_ai/infrastructure/database/models/__init__.py +41 -0
- narrative_ai/infrastructure/database/models/analytics_event_model.py +23 -0
- narrative_ai/infrastructure/database/models/audio_file_model.py +32 -0
- narrative_ai/infrastructure/database/models/audit_log_model.py +35 -0
- narrative_ai/infrastructure/database/models/base.py +24 -0
- narrative_ai/infrastructure/database/models/conversation_message_model.py +26 -0
- narrative_ai/infrastructure/database/models/conversation_model.py +29 -0
- narrative_ai/infrastructure/database/models/diary_embedding_model.py +34 -0
- narrative_ai/infrastructure/database/models/diary_entry_model.py +37 -0
- narrative_ai/infrastructure/database/models/entry_media_model.py +25 -0
- narrative_ai/infrastructure/database/models/export_job_model.py +22 -0
- narrative_ai/infrastructure/database/models/subscription_model.py +21 -0
- narrative_ai/infrastructure/database/models/user_model.py +28 -0
- narrative_ai/infrastructure/database/models/user_preference_model.py +29 -0
- narrative_ai/infrastructure/database/repositories/__init__.py +17 -0
- narrative_ai/infrastructure/database/repositories/analytics_event_repository_impl.py +40 -0
- narrative_ai/infrastructure/database/repositories/audio_repository_impl.py +98 -0
- narrative_ai/infrastructure/database/repositories/base_repository.py +64 -0
- narrative_ai/infrastructure/database/repositories/conversation_repository_impl.py +148 -0
- narrative_ai/infrastructure/database/repositories/entry_media_repository_impl.py +58 -0
- narrative_ai/infrastructure/database/repositories/entry_repository_impl.py +244 -0
- narrative_ai/infrastructure/database/repositories/search_repository_impl.py +121 -0
- narrative_ai/infrastructure/database/repositories/user_repository_impl.py +106 -0
- narrative_ai/infrastructure/database/schema.py +42 -0
- narrative_ai/infrastructure/database/session.py +64 -0
- narrative_ai/infrastructure/external/__init__.py +0 -0
- narrative_ai/infrastructure/external/email_client.py +0 -0
- narrative_ai/infrastructure/external/llm_client.py +0 -0
- narrative_ai/infrastructure/external/notification_client.py +0 -0
- narrative_ai/infrastructure/external/stt_client.py +0 -0
- narrative_ai/infrastructure/security/__init__.py +0 -0
- narrative_ai/infrastructure/security/auth_service.py +0 -0
- narrative_ai/infrastructure/security/config.py +0 -0
- narrative_ai/infrastructure/security/encryption.py +0 -0
- narrative_ai/infrastructure/security/jwt_service.py +113 -0
- narrative_ai/infrastructure/security/password_hasher.py +37 -0
- narrative_ai/infrastructure/security/rate_limiter.py +0 -0
- narrative_ai/infrastructure/storage/__init__.py +0 -0
- narrative_ai/infrastructure/storage/azure_storage.py +0 -0
- narrative_ai/infrastructure/storage/base_storage.py +0 -0
- narrative_ai/infrastructure/storage/config.py +0 -0
- narrative_ai/infrastructure/storage/gcs_storage.py +0 -0
- narrative_ai/infrastructure/storage/local_storage.py +0 -0
- narrative_ai/infrastructure/storage/s3_storage.py +0 -0
- narrative_ai/infrastructure/vector/__init__.py +31 -0
- narrative_ai/infrastructure/vector/base_vector_store.py +153 -0
- narrative_ai/infrastructure/vector/config.py +39 -0
- narrative_ai/infrastructure/vector/milvus_store.py +0 -0
- narrative_ai/infrastructure/vector/pgvector_store.py +120 -0
- narrative_ai/infrastructure/vector/pinecone_store.py +0 -0
- narrative_ai/infrastructure/vector/qdrant_store.py +424 -0
- narrative_ai/models/__init__.py +10 -0
- narrative_ai/models/registry.py +268 -0
- narrative_ai/security/__init__.py +67 -0
- narrative_ai/security/audit_trail.py +834 -0
- narrative_ai/security/circuit_breaker.py +179 -0
- narrative_ai/security/engine_integration.py +251 -0
- narrative_ai/security/error_handling.py +439 -0
- narrative_ai/security/input_validation.py +513 -0
- narrative_ai/security/rate_limiting.py +1069 -0
- narrative_ai/security/stream_isolation.py +389 -0
- narrative_ai/shared/__init__.py +0 -0
- narrative_ai/shared/constants.py +0 -0
- narrative_ai/shared/crypto.py +0 -0
- narrative_ai/shared/date_utils.py +0 -0
- narrative_ai/shared/decorators.py +0 -0
- narrative_ai/shared/exceptions.py +0 -0
- narrative_ai/shared/jwt_utils.py +0 -0
- narrative_ai/shared/logger.py +0 -0
- narrative_ai/shared/retry_utils.py +62 -0
- narrative_ai/shared/typing.py +0 -0
- narrative_ai/shared/validators.py +0 -0
- narrative_ai_framework-0.1.0.dist-info/METADATA +211 -0
- narrative_ai_framework-0.1.0.dist-info/RECORD +402 -0
- narrative_ai_framework-0.1.0.dist-info/WHEEL +5 -0
- narrative_ai_framework-0.1.0.dist-info/entry_points.txt +6 -0
- narrative_ai_framework-0.1.0.dist-info/licenses/LICENSE +21 -0
- 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}")
|