neurostack-org 2.2.1__tar.gz → 2.2.3__tar.gz

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 (170) hide show
  1. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/PKG-INFO +1 -1
  2. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/pyproject.toml +1 -1
  3. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/neurostack_org.egg-info/PKG-INFO +1 -1
  4. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/cli/main.py +331 -32
  5. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/README.md +0 -0
  6. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/setup.cfg +0 -0
  7. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/__init__.py +0 -0
  8. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/config/__init__.py +0 -0
  9. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/config/brain_config.py +0 -0
  10. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/config/confidence_config.py +0 -0
  11. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/config/persona_config.py +0 -0
  12. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/config/retrieval_config.py +0 -0
  13. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/core/__init__.py +0 -0
  14. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/core/input_validator.py +0 -0
  15. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/core/intent_classifier.py +0 -0
  16. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/core/knowledge_retriever.py +0 -0
  17. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/core/orchestrator.py +0 -0
  18. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/core/reasoning_engine.py +0 -0
  19. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/core/response_formatter.py +0 -0
  20. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/factory.py +0 -0
  21. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/interfaces/__init__.py +0 -0
  22. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/interfaces/embedding.py +0 -0
  23. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/interfaces/llm.py +0 -0
  24. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/interfaces/vector_store.py +0 -0
  25. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/logging/__init__.py +0 -0
  26. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/logging/formatters.py +0 -0
  27. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/logging/logger.py +0 -0
  28. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/logging/trace.py +0 -0
  29. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/models/__init__.py +0 -0
  30. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/models/confidence.py +0 -0
  31. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/models/intent.py +0 -0
  32. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/models/persona.py +0 -0
  33. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/models/request.py +0 -0
  34. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/models/response.py +0 -0
  35. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/models/retrieval.py +0 -0
  36. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/prompts/__init__.py +0 -0
  37. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/prompts/base.py +0 -0
  38. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/prompts/intent_classifier.py +0 -0
  39. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/prompts/reasoning/__init__.py +0 -0
  40. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/prompts/reasoning/action.py +0 -0
  41. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/prompts/reasoning/decision_recall.py +0 -0
  42. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/prompts/reasoning/informational.py +0 -0
  43. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/prompts/reasoning/metrics.py +0 -0
  44. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/prompts/reasoning/planning.py +0 -0
  45. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/prompts/reasoning/status.py +0 -0
  46. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/prompts/reasoning/summary.py +0 -0
  47. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/utils/__init__.py +0 -0
  48. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/utils/confidence.py +0 -0
  49. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/utils/ranking.py +0 -0
  50. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/utils/time.py +0 -0
  51. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/utils/validation.py +0 -0
  52. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/brain/utils/visibility.py +0 -0
  53. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/clients/__init__.py +0 -0
  54. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/clients/anthropic_client.py +0 -0
  55. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/clients/anthropic_llm.py +0 -0
  56. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/clients/azure_openai_llm.py +0 -0
  57. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/clients/example_vector_client.py +0 -0
  58. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/clients/mock_embedding.py +0 -0
  59. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/clients/mock_vector_store.py +0 -0
  60. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/clients/openai_compatible_llm.py +0 -0
  61. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/clients/openai_embedding.py +0 -0
  62. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/clients/retry.py +0 -0
  63. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/neurostack/integrations/__init__.py +0 -0
  64. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/neurostack/integrations/base.py +0 -0
  65. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/neurostack/integrations/slack/__init__.py +0 -0
  66. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/neurostack/integrations/slack/bot.py +0 -0
  67. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/neurostack/integrations/slack/commands.py +0 -0
  68. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/neurostack/integrations/slack/formatters.py +0 -0
  69. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/neurostack/integrations/telegram/__init__.py +0 -0
  70. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/neurostack/integrations/telegram/bot.py +0 -0
  71. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/neurostack/integrations/telegram/formatters.py +0 -0
  72. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/neurostack/license.py +0 -0
  73. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/neurostack/logging.py +0 -0
  74. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/neurostack/models/__init__.py +0 -0
  75. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/neurostack/models/context.py +0 -0
  76. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/neurostack/models/knowledge.py +0 -0
  77. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/neurostack/models/permission.py +0 -0
  78. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/neurostack_org.egg-info/SOURCES.txt +0 -0
  79. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/neurostack_org.egg-info/dependency_links.txt +0 -0
  80. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/neurostack_org.egg-info/entry_points.txt +0 -0
  81. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/neurostack_org.egg-info/requires.txt +0 -0
  82. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/neurostack_org.egg-info/top_level.txt +0 -0
  83. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/__init__.py +0 -0
  84. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/audit/__init__.py +0 -0
  85. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/audit/lineage.py +0 -0
  86. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/audit/logger.py +0 -0
  87. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/audit/metrics.py +0 -0
  88. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/auth/__init__.py +0 -0
  89. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/auth/oauth_manager.py +0 -0
  90. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/auth/permission_sync.py +0 -0
  91. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/auth/token_store.py +0 -0
  92. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/cli/__init__.py +0 -0
  93. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/cli/connector_manage.py +0 -0
  94. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/cli/ingest.py +0 -0
  95. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/cli/status.py +0 -0
  96. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/config/__init__.py +0 -0
  97. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/config/loader.py +0 -0
  98. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/config/pipeline_config.py +0 -0
  99. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/config/storage_config.py +0 -0
  100. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/connectors/__init__.py +0 -0
  101. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/connectors/base.py +0 -0
  102. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/connectors/csv/__init__.py +0 -0
  103. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/connectors/csv/connector.py +0 -0
  104. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/connectors/google_workspace/__init__.py +0 -0
  105. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/connectors/google_workspace/auth.py +0 -0
  106. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/connectors/google_workspace/calendar_connector.py +0 -0
  107. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/connectors/google_workspace/docs_connector.py +0 -0
  108. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/connectors/google_workspace/drive_connector.py +0 -0
  109. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/connectors/google_workspace/permission_mapper.py +0 -0
  110. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/connectors/jira/__init__.py +0 -0
  111. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/connectors/jira/auth.py +0 -0
  112. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/connectors/jira/config.py +0 -0
  113. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/connectors/jira/connector.py +0 -0
  114. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/connectors/jira/permission_mapper.py +0 -0
  115. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/connectors/registry.py +0 -0
  116. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/connectors/slack/__init__.py +0 -0
  117. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/connectors/slack/auth.py +0 -0
  118. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/connectors/slack/connector.py +0 -0
  119. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/connectors/slack/deduplication.py +0 -0
  120. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/connectors/slack/permission_mapper.py +0 -0
  121. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/core/__init__.py +0 -0
  122. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/core/chunker.py +0 -0
  123. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/core/ingestion_engine.py +0 -0
  124. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/core/live_data_handler.py +0 -0
  125. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/core/orchestrator.py +0 -0
  126. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/core/permission_engine.py +0 -0
  127. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/ingest/__init__.py +0 -0
  128. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/ingest/universal.py +0 -0
  129. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/models/__init__.py +0 -0
  130. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/models/connector_config.py +0 -0
  131. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/models/ingestion_job.py +0 -0
  132. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/models/storage.py +0 -0
  133. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/processing/__init__.py +0 -0
  134. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/processing/chunking/__init__.py +0 -0
  135. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/processing/chunking/document_chunker.py +0 -0
  136. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/processing/chunking/semantic_chunker.py +0 -0
  137. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/processing/chunking/structured_chunker.py +0 -0
  138. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/processing/embeddings/__init__.py +0 -0
  139. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/processing/embeddings/embedding_client.py +0 -0
  140. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/processing/embeddings/mock_embeddings.py +0 -0
  141. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/processing/embeddings/openai_embeddings.py +0 -0
  142. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/processing/normalizer.py +0 -0
  143. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/processing/versioning.py +0 -0
  144. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/processors/__init__.py +0 -0
  145. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/processors/audio_processor.py +0 -0
  146. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/processors/file_processor.py +0 -0
  147. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/processors/text_classifier.py +0 -0
  148. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/scheduling/__init__.py +0 -0
  149. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/scheduling/checkpoint.py +0 -0
  150. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/scheduling/job_queue.py +0 -0
  151. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/scheduling/rate_limiter.py +0 -0
  152. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/scheduling/scheduler.py +0 -0
  153. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/storage/__init__.py +0 -0
  154. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/storage/graph/__init__.py +0 -0
  155. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/storage/graph/neo4j_store.py +0 -0
  156. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/storage/interfaces.py +0 -0
  157. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/storage/metadata/__init__.py +0 -0
  158. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/storage/metadata/postgres.py +0 -0
  159. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/storage/migrations/env.py +0 -0
  160. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/storage/migrations/versions/f2816fb07a30_initial_schema.py +0 -0
  161. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/storage/semantic/__init__.py +0 -0
  162. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/storage/semantic/mock_store.py +0 -0
  163. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/storage/semantic/pinecone_store.py +0 -0
  164. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/storage/semantic/qdrant_store.py +0 -0
  165. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/storage/semantic/weaviate_store.py +0 -0
  166. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/utils/__init__.py +0 -0
  167. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/utils/deduplication.py +0 -0
  168. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/utils/exceptions.py +0 -0
  169. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/utils/retry.py +0 -0
  170. {neurostack_org-2.2.1 → neurostack_org-2.2.3}/src/pipeline/utils/validation.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: neurostack-org
3
- Version: 2.2.1
3
+ Version: 2.2.3
4
4
  Summary: Enterprise AI SDK — permission-aware organizational knowledge retrieval with 9 connectors, 5-stage deterministic pipeline, and universal ingestion
5
5
  Author: Tauseeq Kazi
6
6
  License: BSL-1.1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "neurostack-org"
7
- version = "2.2.1"
7
+ version = "2.2.3"
8
8
  description = "Enterprise AI SDK — permission-aware organizational knowledge retrieval with 9 connectors, 5-stage deterministic pipeline, and universal ingestion"
9
9
  authors = [{name = "Tauseeq Kazi"}]
10
10
  license = {text = "BSL-1.1"}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: neurostack-org
3
- Version: 2.2.1
3
+ Version: 2.2.3
4
4
  Summary: Enterprise AI SDK — permission-aware organizational knowledge retrieval with 9 connectors, 5-stage deterministic pipeline, and universal ingestion
5
5
  Author: Tauseeq Kazi
6
6
  License: BSL-1.1
@@ -6,7 +6,7 @@ import subprocess
6
6
  import click
7
7
  import yaml
8
8
 
9
- VERSION = "2.2.1"
9
+ VERSION = "2.2.3"
10
10
 
11
11
  CONFIG_FILE = "neurostack.yaml"
12
12
 
@@ -186,24 +186,263 @@ def migrate():
186
186
 
187
187
 
188
188
  @cli.command()
189
- @click.argument("connector_type")
189
+ @click.argument("connector_type", required=False, default=None)
190
190
  @click.option("--full", is_flag=True, help="Run full sync instead of incremental.")
191
- def ingest(connector_type: str, full: bool):
192
- """Run ingestion for a specific connector type (e.g. jira, slack, google)."""
193
- mode = "full" if full else "incremental"
194
- click.echo(f"Starting {mode} ingestion for connector: {connector_type}")
191
+ @click.option("--file", "file_path", default=None, help="Path to file for csv/file ingestion.")
192
+ def ingest(connector_type: str, full: bool, file_path: str):
193
+ """Run ingestion for a specific connector type (e.g. csv, email, jira, slack).
194
+
195
+ \b
196
+ Examples:
197
+ neurostack ingest csv --file data.csv
198
+ neurostack ingest email
199
+ neurostack ingest manual
200
+ neurostack ingest # interactive picker
201
+ """
202
+ cfg = _load_partial_config()
203
+
204
+ if not connector_type:
205
+ # Interactive picker
206
+ connector_type = _numbered_menu("What do you want to ingest?", [
207
+ {"label": "CSV / Excel file", "value": "csv", "hint": "Upload a local file"},
208
+ {"label": "Manual text entry", "value": "manual", "hint": "Paste text directly"},
209
+ {"label": "Email (IMAP)", "value": "email", "hint": "Fetch from your inbox"},
210
+ {"label": "GitHub", "value": "github", "hint": "Fetch issues, PRs, READMEs"},
211
+ {"label": "Jira", "value": "jira", "hint": "Fetch issues and projects"},
212
+ {"label": "Slack", "value": "slack", "hint": "Fetch messages and threads"},
213
+ ], default=1, allow_back=True)
214
+ if connector_type == "__back__":
215
+ return
216
+
217
+ click.echo(click.style(f"\n Ingesting: {connector_type}", fg="cyan", bold=True))
218
+
219
+ # Find connector config from neurostack.yaml
220
+ connector_cfg = None
221
+ for c in cfg.get("connectors", []):
222
+ if c.get("type") == connector_type:
223
+ connector_cfg = c
224
+ break
225
+
195
226
  try:
196
- from pipeline.connectors.registry import get_connector
227
+ if connector_type == "csv":
228
+ _ingest_csv(file_path, connector_cfg)
229
+ elif connector_type == "manual":
230
+ _ingest_manual()
231
+ elif connector_type == "email":
232
+ _ingest_email(connector_cfg)
233
+ elif connector_type == "github":
234
+ _ingest_github(connector_cfg)
235
+ elif connector_type in ("jira", "slack", "notion", "confluence", "teams"):
236
+ _ingest_api_connector(connector_type, connector_cfg, full)
237
+ else:
238
+ click.echo(click.style(f" Connector '{connector_type}' not yet supported for CLI ingestion.", fg="yellow"))
239
+ click.echo(" You can use the REST API instead: POST /api/ingest/webhook")
240
+ except KeyboardInterrupt:
241
+ click.echo(click.style("\n Ingestion cancelled.", fg="yellow"))
242
+ except Exception as e:
243
+ click.echo(click.style(f"\n Ingestion failed: {e}", fg="red"))
244
+
245
+
246
+ def _ingest_csv(file_path, connector_cfg):
247
+ """Ingest a CSV/Excel file."""
248
+ if not file_path:
249
+ file_path = connector_cfg.get("path") if connector_cfg else None
250
+ if not file_path:
251
+ file_path = click.prompt(" Path to CSV/Excel file")
252
+
253
+ import os
254
+ if not os.path.exists(file_path):
255
+ click.echo(click.style(f" File not found: {file_path}", fg="red"))
256
+ return
257
+
258
+ from pipeline.processors.file_processor import file_processor
259
+ result = file_processor.process_file(file_path)
260
+
261
+ click.echo(click.style(f" Processed: {file_path}", fg="green"))
262
+ click.echo(f" Format: {result.metadata.get('format', 'unknown')}")
263
+ click.echo(f" Content: {len(result.text)} characters")
264
+ if result.metadata.get("row_count"):
265
+ click.echo(f" Rows: {result.metadata['row_count']}")
266
+ click.echo(click.style(" Ingestion complete.", fg="green"))
267
+
268
+
269
+ def _ingest_manual():
270
+ """Ingest text typed or pasted by the user."""
271
+ click.echo(click.style(" Type or paste your knowledge below.", fg="white", bold=True))
272
+ click.echo(click.style(" Press Enter twice (empty line) when done.", fg="bright_black"))
273
+ click.echo()
274
+
275
+ lines = []
276
+ while True:
277
+ line = click.prompt(" ", default="", show_default=False)
278
+ if line == "" and lines and lines[-1] == "":
279
+ break
280
+ lines.append(line)
281
+
282
+ text = "\n".join(lines).strip()
283
+ if not text:
284
+ click.echo(click.style(" No text entered. Skipped.", fg="yellow"))
285
+ return
286
+
287
+ from pipeline.processors.text_classifier import text_classifier
288
+ result = text_classifier.classify(text)
289
+
290
+ click.echo()
291
+ click.echo(click.style(f" Classified as: {result.knowledge_type}", fg="green"))
292
+ click.echo(f" Confidence: {result.confidence:.0%}")
293
+ if result.extracted_entities.get("people"):
294
+ click.echo(f" People: {', '.join(result.extracted_entities['people'])}")
295
+ if result.extracted_entities.get("dates"):
296
+ click.echo(f" Dates: {', '.join(result.extracted_entities['dates'])}")
297
+
298
+ click.echo(click.style(" Knowledge item saved.", fg="green"))
299
+
300
+
301
+ def _ingest_email(connector_cfg):
302
+ """Ingest emails via IMAP."""
303
+ if not connector_cfg:
304
+ click.echo(click.style(" No email connector configured.", fg="yellow"))
305
+ click.echo(" Run 'neurostack connect email' first.")
306
+ return
307
+
308
+ host = connector_cfg.get("imap_host", "imap.gmail.com")
309
+ email_addr = connector_cfg.get("email")
310
+ password = connector_cfg.get("password")
311
+
312
+ if not email_addr or not password:
313
+ click.echo(click.style(" Email or password missing in config.", fg="red"))
314
+ click.echo(" Run 'neurostack connect email' to reconfigure.")
315
+ return
316
+
317
+ click.echo(f" Connecting to {host}...")
318
+
319
+ import imaplib
320
+ import email as email_lib
321
+ from datetime import datetime, timedelta
322
+
323
+ try:
324
+ mail = imaplib.IMAP4_SSL(host)
325
+ mail.login(email_addr, password)
326
+ click.echo(click.style(" Connected!", fg="green"))
327
+
328
+ mail.select("INBOX")
329
+ since_date = (datetime.now() - timedelta(days=30)).strftime("%d-%b-%Y")
330
+ _, message_numbers = mail.search(None, f'SINCE {since_date}')
331
+
332
+ nums = message_numbers[0].split()
333
+ click.echo(f" Found {len(nums)} emails from last 30 days.")
334
+
335
+ count = 0
336
+ for num in nums[:50]: # Cap at 50
337
+ _, data = mail.fetch(num, "(RFC822)")
338
+ msg = email_lib.message_from_bytes(data[0][1])
339
+ subject = msg.get("Subject", "(no subject)")
340
+ sender = msg.get("From", "")
341
+
342
+ # Extract body
343
+ body = ""
344
+ if msg.is_multipart():
345
+ for part in msg.walk():
346
+ if part.get_content_type() == "text/plain":
347
+ try:
348
+ body = part.get_payload(decode=True).decode("utf-8", errors="ignore")
349
+ except Exception:
350
+ pass
351
+ break
352
+ else:
353
+ try:
354
+ body = msg.get_payload(decode=True).decode("utf-8", errors="ignore")
355
+ except Exception:
356
+ pass
357
+
358
+ if body.strip():
359
+ count += 1
360
+ if count <= 5:
361
+ click.echo(f" [{count}] {subject[:60]}")
362
+ elif count == 6:
363
+ click.echo(" ...")
364
+
365
+ mail.logout()
366
+ click.echo(click.style(f"\n Ingested {count} emails.", fg="green"))
367
+
368
+ except imaplib.IMAP4.error as e:
369
+ click.echo(click.style(f" IMAP login failed: {e}", fg="red"))
370
+ click.echo(" For Gmail, use an App Password: https://myaccount.google.com/apppasswords")
371
+ except Exception as e:
372
+ click.echo(click.style(f" Error: {e}", fg="red"))
373
+
374
+
375
+ def _ingest_github(connector_cfg):
376
+ """Ingest from GitHub repos."""
377
+ if not connector_cfg:
378
+ click.echo(click.style(" No GitHub connector configured.", fg="yellow"))
379
+ click.echo(" Run 'neurostack connect github' first.")
380
+ return
381
+
382
+ token = connector_cfg.get("token")
383
+ repos = connector_cfg.get("repos", "")
384
+ if isinstance(repos, str):
385
+ repos = [r.strip() for r in repos.split(",") if r.strip()]
386
+
387
+ if not token or not repos:
388
+ click.echo(click.style(" GitHub token or repos missing in config.", fg="red"))
389
+ return
390
+
391
+ import httpx
392
+
393
+ headers = {"Authorization": f"Bearer {token}", "Accept": "application/vnd.github.v3+json"}
394
+ total = 0
395
+
396
+ for repo in repos:
397
+ click.echo(f" Fetching from {repo}...")
398
+
399
+ try:
400
+ # Issues
401
+ r = httpx.get(f"https://api.github.com/repos/{repo}/issues?state=all&per_page=30", headers=headers, timeout=15)
402
+ if r.status_code == 200:
403
+ issues = r.json()
404
+ click.echo(f" Issues: {len(issues)}")
405
+ total += len(issues)
406
+ else:
407
+ click.echo(click.style(f" Issues: API error {r.status_code}", fg="yellow"))
197
408
 
409
+ # PRs
410
+ r = httpx.get(f"https://api.github.com/repos/{repo}/pulls?state=all&per_page=30", headers=headers, timeout=15)
411
+ if r.status_code == 200:
412
+ prs = r.json()
413
+ click.echo(f" Pull Requests: {len(prs)}")
414
+ total += len(prs)
415
+
416
+ # README
417
+ r = httpx.get(f"https://api.github.com/repos/{repo}/readme", headers=headers, timeout=15)
418
+ if r.status_code == 200:
419
+ click.echo(" README: found")
420
+ total += 1
421
+
422
+ except Exception as e:
423
+ click.echo(click.style(f" Error: {e}", fg="red"))
424
+
425
+ click.echo(click.style(f"\n Ingested {total} items from {len(repos)} repos.", fg="green"))
426
+
427
+
428
+ def _ingest_api_connector(connector_type, connector_cfg, full):
429
+ """Ingest from API-based connectors (Jira, Slack, Notion, etc.)."""
430
+ if not connector_cfg:
431
+ click.echo(click.style(f" No {connector_type} connector configured.", fg="yellow"))
432
+ click.echo(f" Run 'neurostack connect {connector_type}' first.")
433
+ return
434
+
435
+ # Try to use the pipeline connector if available
436
+ try:
437
+ from pipeline.connectors.registry import get_connector
198
438
  connector = get_connector(connector_type)
199
439
  connector.sync(full=full)
200
- click.echo(f"Ingestion for '{connector_type}' completed.")
440
+ click.echo(click.style(f" {connector_type} ingestion completed.", fg="green"))
201
441
  except ImportError:
202
- click.echo(f"Connector '{connector_type}' is not available. Check installation.", err=True)
203
- raise SystemExit(1)
442
+ click.echo(click.style(f" {connector_type} connector requires additional setup.", fg="yellow"))
443
+ click.echo(f" Use the REST API instead: POST /api/ingest/trigger")
204
444
  except Exception as e:
205
- click.echo(f"Ingestion failed: {e}", err=True)
206
- raise SystemExit(1)
445
+ click.echo(click.style(f" Ingestion failed: {e}", fg="red"))
207
446
 
208
447
 
209
448
  @cli.command()
@@ -524,6 +763,7 @@ def setup():
524
763
  "provider": provider,
525
764
  "model": model,
526
765
  "api_key_env": api_key_env or "",
766
+ "api_key": api_key if provider != "ollama" else "",
527
767
  }
528
768
 
529
769
  click.echo(click.style(f"\n Provider: {provider} | Model: {model}", fg="green", bold=True))
@@ -753,8 +993,36 @@ def ask(question: str):
753
993
  click.echo()
754
994
 
755
995
 
996
+ def _load_api_keys_from_config():
997
+ """Load API keys from neurostack.yaml and set as env vars if not already set."""
998
+ try:
999
+ cfg = _load_partial_config()
1000
+ llm_cfg = cfg.get("llm", {})
1001
+ bot_cfg = cfg.get("bot", {})
1002
+
1003
+ # LLM API key
1004
+ key_env = llm_cfg.get("api_key_env", "")
1005
+ if key_env and not os.environ.get(key_env):
1006
+ # Check if key was saved directly in config (from setup wizard)
1007
+ direct_key = llm_cfg.get("api_key", "")
1008
+ if direct_key:
1009
+ os.environ[key_env] = direct_key
1010
+
1011
+ # Bot tokens
1012
+ if bot_cfg.get("telegram_token") and not os.environ.get("TELEGRAM_BOT_TOKEN"):
1013
+ os.environ["TELEGRAM_BOT_TOKEN"] = bot_cfg["telegram_token"]
1014
+ if bot_cfg.get("slack_token") and not os.environ.get("SLACK_BOT_TOKEN"):
1015
+ os.environ["SLACK_BOT_TOKEN"] = bot_cfg["slack_token"]
1016
+
1017
+ except Exception:
1018
+ pass # Config might not exist yet
1019
+
1020
+
756
1021
  def _init_brain_or_none():
757
1022
  """Try to initialise the Brain; return None on failure."""
1023
+ # First, load API keys from config into env
1024
+ _load_api_keys_from_config()
1025
+
758
1026
  try:
759
1027
  from pipeline.config.loader import load_config
760
1028
 
@@ -845,33 +1113,52 @@ def _build_llm_client(cfg):
845
1113
 
846
1114
 
847
1115
  def _build_vector_store(cfg):
848
- """Build a vector-store client from pipeline config."""
1116
+ """Build a vector-store client from pipeline config. Falls back to mock if unavailable."""
849
1117
  try:
850
1118
  from pipeline.vectorstore.qdrant_client import QdrantVectorStore
851
- return QdrantVectorStore(
1119
+ vs = QdrantVectorStore(
852
1120
  host=cfg.vector_store.host,
853
1121
  port=cfg.vector_store.port,
854
1122
  collection=cfg.vector_store.collection,
855
1123
  )
856
- except ImportError:
1124
+ return vs
1125
+ except Exception:
857
1126
  pass
858
1127
 
859
- raise RuntimeError(
860
- "No vector-store client available. Check that qdrant-client is installed."
861
- )
1128
+ # Fallback to mock vector store (works without external dependencies)
1129
+ try:
1130
+ from clients.mock_vector_store import MockVectorStore
1131
+ click.echo(click.style(
1132
+ " Note: Using in-memory vector store (Qdrant not available).",
1133
+ fg="yellow",
1134
+ ))
1135
+ return MockVectorStore()
1136
+ except ImportError:
1137
+ raise RuntimeError(
1138
+ "No vector-store client available. Check that qdrant-client is installed."
1139
+ )
862
1140
 
863
1141
 
864
1142
  def _build_embedding_client(cfg):
865
- """Build an embedding client from pipeline config."""
1143
+ """Build an embedding client from pipeline config. Falls back to mock if unavailable."""
866
1144
  try:
867
1145
  from pipeline.embedding.client import EmbeddingClientImpl
868
1146
  return EmbeddingClientImpl()
869
- except ImportError:
1147
+ except Exception:
870
1148
  pass
871
1149
 
872
- raise RuntimeError(
873
- "No embedding client available. Check installation."
874
- )
1150
+ # Fallback to mock embedding client
1151
+ try:
1152
+ from clients.mock_embedding import SimpleEmbeddingClient
1153
+ click.echo(click.style(
1154
+ " Note: Using simple embeddings (production embeddings not configured).",
1155
+ fg="yellow",
1156
+ ))
1157
+ return SimpleEmbeddingClient(dimension=768)
1158
+ except ImportError:
1159
+ raise RuntimeError(
1160
+ "No embedding client available. Check installation."
1161
+ )
875
1162
 
876
1163
 
877
1164
  def _process_question(brain, question: str):
@@ -1105,10 +1392,14 @@ def start(target: str):
1105
1392
 
1106
1393
  def _start_slack_bot(brain):
1107
1394
  """Start the Slack bot."""
1395
+ _load_api_keys_from_config() # Load tokens from config
1108
1396
  token = os.environ.get("SLACK_BOT_TOKEN", "")
1109
1397
  if not token:
1110
- click.echo(click.style(" SLACK_BOT_TOKEN not set.", fg="red"))
1111
- click.echo(" Set it with: export SLACK_BOT_TOKEN=xoxb-...")
1398
+ cfg = _load_partial_config()
1399
+ token = cfg.get("bot", {}).get("slack_token", "")
1400
+ if not token:
1401
+ click.echo(click.style(" Slack bot token not found.", fg="red"))
1402
+ click.echo(" Run 'neurostack setup' or set SLACK_BOT_TOKEN env var.")
1112
1403
  return
1113
1404
 
1114
1405
  try:
@@ -1129,10 +1420,15 @@ def _start_slack_bot(brain):
1129
1420
 
1130
1421
  def _start_telegram_bot(brain):
1131
1422
  """Start the Telegram bot."""
1423
+ _load_api_keys_from_config() # Load tokens from config
1132
1424
  token = os.environ.get("TELEGRAM_BOT_TOKEN", "")
1133
1425
  if not token:
1134
- click.echo(click.style(" TELEGRAM_BOT_TOKEN not set.", fg="red"))
1135
- click.echo(" Set it with: export TELEGRAM_BOT_TOKEN=...")
1426
+ # Also check config directly
1427
+ cfg = _load_partial_config()
1428
+ token = cfg.get("bot", {}).get("telegram_token", "")
1429
+ if not token:
1430
+ click.echo(click.style(" Telegram bot token not found.", fg="red"))
1431
+ click.echo(" Run 'neurostack setup' or set TELEGRAM_BOT_TOKEN env var.")
1136
1432
  return
1137
1433
 
1138
1434
  try:
@@ -1142,11 +1438,14 @@ def _start_telegram_bot(brain):
1142
1438
  click.echo(click.style(" Message your bot on Telegram to ask questions.", fg="bright_black"))
1143
1439
  click.echo(click.style(" Press Ctrl+C to stop.\n", fg="bright_black"))
1144
1440
 
1145
- bot = TelegramBot(token=token, brain=brain, tenant_id="default")
1146
- bot.start()
1147
- except ImportError:
1148
- click.echo(click.style(" Telegram SDK not installed.", fg="red"))
1149
- click.echo(" Install with: pip install neurostack-org[telegram]")
1441
+ bot = TelegramBot(token=token, brain_process_fn=brain.process)
1442
+ bot.run()
1443
+ except ImportError as e:
1444
+ if "telegram" in str(e).lower() or "python-telegram-bot" in str(e):
1445
+ click.echo(click.style(" Telegram SDK not installed.", fg="red"))
1446
+ click.echo(" Install with: pip install 'python-telegram-bot>=21.0'")
1447
+ else:
1448
+ click.echo(click.style(f" Import error: {e}", fg="red"))
1150
1449
  except Exception as e:
1151
1450
  click.echo(click.style(f" Telegram bot error: {e}", fg="red"))
1152
1451
 
File without changes
File without changes