trustgraph-base 2.3.14__tar.gz → 2.3.16__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.
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/PKG-INFO +2 -1
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/pyproject.toml +1 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/consumer.py +11 -0
- trustgraph_base-2.3.16/trustgraph/base/kafka_backend.py +452 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/pubsub.py +57 -0
- trustgraph_base-2.3.16/trustgraph/base_version.py +1 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph_base.egg-info/PKG-INFO +2 -1
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph_base.egg-info/SOURCES.txt +1 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph_base.egg-info/requires.txt +1 -0
- trustgraph_base-2.3.14/trustgraph/base_version.py +0 -1
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/README.md +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/setup.cfg +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/api/__init__.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/api/api.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/api/async_bulk_client.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/api/async_flow.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/api/async_metrics.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/api/async_socket_client.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/api/bulk_client.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/api/collection.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/api/config.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/api/exceptions.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/api/explainability.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/api/flow.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/api/knowledge.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/api/library.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/api/metrics.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/api/socket_client.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/api/types.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/__init__.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/agent_client.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/agent_service.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/async_processor.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/backend.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/cassandra_config.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/chunking_service.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/collection_config_handler.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/config_client.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/consumer_spec.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/document_embeddings_client.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/document_embeddings_query_service.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/document_embeddings_store_service.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/dynamic_tool_service.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/embeddings_client.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/embeddings_service.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/flow.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/flow_processor.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/graph_embeddings_client.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/graph_embeddings_query_service.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/graph_embeddings_store_service.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/graph_rag_client.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/librarian_client.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/llm_service.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/logging.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/metrics.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/parameter_spec.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/processor_group.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/producer.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/producer_spec.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/prompt_client.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/publisher.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/pulsar_backend.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/rabbitmq_backend.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/request_response_spec.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/row_embeddings_query_client.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/serialization.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/spec.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/structured_query_client.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/subscriber.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/subscriber_spec.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/text_completion_client.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/tool_client.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/tool_service.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/tool_service_client.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/triples_client.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/triples_query_service.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/base/triples_store_service.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/clients/__init__.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/clients/agent_client.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/clients/base.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/clients/config_client.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/clients/document_embeddings_client.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/clients/document_rag_client.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/clients/embeddings_client.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/clients/graph_embeddings_client.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/clients/graph_rag_client.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/clients/llm_client.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/clients/prompt_client.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/clients/row_embeddings_client.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/clients/triples_query_client.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/exceptions.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/i18n/__init__.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/i18n/packs/__init__.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/i18n/packs/ar.json +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/i18n/packs/en.json +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/i18n/packs/es.json +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/i18n/packs/he.json +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/i18n/packs/hi.json +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/i18n/packs/pt.json +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/i18n/packs/ru.json +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/i18n/packs/sw.json +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/i18n/packs/tr.json +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/i18n/packs/zh-cn.json +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/knowledge/__init__.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/knowledge/defs.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/knowledge/document.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/knowledge/identifier.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/knowledge/organization.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/knowledge/publication.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/log_level.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/messaging/__init__.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/messaging/registry.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/messaging/translators/__init__.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/messaging/translators/agent.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/messaging/translators/base.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/messaging/translators/collection.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/messaging/translators/config.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/messaging/translators/diagnosis.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/messaging/translators/document_loading.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/messaging/translators/embeddings.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/messaging/translators/embeddings_query.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/messaging/translators/flow.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/messaging/translators/knowledge.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/messaging/translators/library.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/messaging/translators/metadata.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/messaging/translators/nlp_query.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/messaging/translators/primitives.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/messaging/translators/prompt.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/messaging/translators/retrieval.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/messaging/translators/rows_query.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/messaging/translators/sparql_query.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/messaging/translators/structured_query.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/messaging/translators/text_completion.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/messaging/translators/tool.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/messaging/translators/triples.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/objects/__init__.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/objects/field.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/objects/object.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/provenance/__init__.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/provenance/agent.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/provenance/namespaces.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/provenance/triples.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/provenance/uris.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/provenance/vocabulary.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/rdf.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/schema/__init__.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/schema/core/__init__.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/schema/core/metadata.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/schema/core/primitives.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/schema/core/topic.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/schema/knowledge/__init__.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/schema/knowledge/document.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/schema/knowledge/embeddings.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/schema/knowledge/graph.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/schema/knowledge/knowledge.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/schema/knowledge/nlp.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/schema/knowledge/object.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/schema/knowledge/rows.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/schema/knowledge/structured.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/schema/services/__init__.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/schema/services/agent.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/schema/services/collection.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/schema/services/config.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/schema/services/diagnosis.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/schema/services/flow.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/schema/services/library.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/schema/services/llm.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/schema/services/lookup.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/schema/services/nlp_query.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/schema/services/prompt.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/schema/services/query.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/schema/services/retrieval.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/schema/services/rows_query.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/schema/services/sparql_query.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/schema/services/storage.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/schema/services/structured_query.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph/schema/services/tool_service.py +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph_base.egg-info/dependency_links.txt +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph_base.egg-info/entry_points.txt +0 -0
- {trustgraph_base-2.3.14 → trustgraph_base-2.3.16}/trustgraph_base.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: trustgraph-base
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.16
|
|
4
4
|
Summary: TrustGraph provides a means to run a pipeline of flexible AI processing components in a flexible means to achieve a processing pipeline.
|
|
5
5
|
Author-email: "trustgraph.ai" <security@trustgraph.ai>
|
|
6
6
|
Project-URL: Homepage, https://github.com/trustgraph-ai/trustgraph
|
|
@@ -13,6 +13,7 @@ Requires-Dist: prometheus-client
|
|
|
13
13
|
Requires-Dist: requests
|
|
14
14
|
Requires-Dist: python-logging-loki
|
|
15
15
|
Requires-Dist: pika
|
|
16
|
+
Requires-Dist: confluent-kafka
|
|
16
17
|
Requires-Dist: pyyaml
|
|
17
18
|
|
|
18
19
|
See https://trustgraph.ai/
|
|
@@ -54,6 +54,17 @@ class Consumer:
|
|
|
54
54
|
self.running = True
|
|
55
55
|
self.consumer_task = None
|
|
56
56
|
|
|
57
|
+
# Kafka topics are created with 1 partition, so multiple
|
|
58
|
+
# consumers in the same group causes rebalance storms where
|
|
59
|
+
# no consumer can fetch. Cap to the backend's limit.
|
|
60
|
+
max_concurrency = getattr(backend, 'max_consumer_concurrency', None)
|
|
61
|
+
if isinstance(max_concurrency, int) and concurrency > max_concurrency:
|
|
62
|
+
logger.info(
|
|
63
|
+
f"Capping concurrency from {concurrency} to "
|
|
64
|
+
f"{max_concurrency} (backend limit)"
|
|
65
|
+
)
|
|
66
|
+
concurrency = max_concurrency
|
|
67
|
+
|
|
57
68
|
self.concurrency = concurrency
|
|
58
69
|
|
|
59
70
|
self.metrics = metrics
|
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Kafka backend implementation for pub/sub abstraction.
|
|
3
|
+
|
|
4
|
+
Each logical topic maps to a Kafka topic. The topic name encodes
|
|
5
|
+
the full identity:
|
|
6
|
+
|
|
7
|
+
class:topicspace:topic -> topicspace.class.topic
|
|
8
|
+
|
|
9
|
+
Producers publish to the topic directly.
|
|
10
|
+
Consumers use consumer groups for competing-consumer semantics:
|
|
11
|
+
|
|
12
|
+
- flow / request: named consumer group (competing consumers)
|
|
13
|
+
- response / notify: unique consumer group per instance, filtering
|
|
14
|
+
messages by correlation ID (all subscribers see all messages)
|
|
15
|
+
|
|
16
|
+
The flow service manages topic lifecycle via AdminClient.
|
|
17
|
+
|
|
18
|
+
Architecture:
|
|
19
|
+
Producer --> [Kafka topic] --> Consumer Group A --> Consumer
|
|
20
|
+
--> Consumer Group A --> Consumer
|
|
21
|
+
--> Consumer Group B --> Consumer (response)
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
import asyncio
|
|
25
|
+
import json
|
|
26
|
+
import logging
|
|
27
|
+
import uuid
|
|
28
|
+
from typing import Any
|
|
29
|
+
|
|
30
|
+
from confluent_kafka import (
|
|
31
|
+
Producer as KafkaProducer,
|
|
32
|
+
Consumer as KafkaConsumer,
|
|
33
|
+
TopicPartition,
|
|
34
|
+
KafkaError,
|
|
35
|
+
KafkaException,
|
|
36
|
+
)
|
|
37
|
+
from confluent_kafka.admin import AdminClient, NewTopic
|
|
38
|
+
|
|
39
|
+
from .backend import PubSubBackend, BackendProducer, BackendConsumer, Message
|
|
40
|
+
from .serialization import dataclass_to_dict, dict_to_dataclass
|
|
41
|
+
|
|
42
|
+
logger = logging.getLogger(__name__)
|
|
43
|
+
|
|
44
|
+
# Retention defaults (milliseconds)
|
|
45
|
+
LONG_RETENTION_MS = 7 * 24 * 60 * 60 * 1000 # 7 days
|
|
46
|
+
SHORT_RETENTION_MS = 300 * 1000 # 5 minutes
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class KafkaMessage:
|
|
50
|
+
"""Wrapper for Kafka messages to match Message protocol."""
|
|
51
|
+
|
|
52
|
+
def __init__(self, msg, schema_cls):
|
|
53
|
+
self._msg = msg
|
|
54
|
+
self._schema_cls = schema_cls
|
|
55
|
+
self._value = None
|
|
56
|
+
|
|
57
|
+
def value(self) -> Any:
|
|
58
|
+
"""Deserialize and return the message value as a dataclass."""
|
|
59
|
+
if self._value is None:
|
|
60
|
+
data_dict = json.loads(self._msg.value().decode('utf-8'))
|
|
61
|
+
self._value = dict_to_dataclass(data_dict, self._schema_cls)
|
|
62
|
+
return self._value
|
|
63
|
+
|
|
64
|
+
def properties(self) -> dict:
|
|
65
|
+
"""Return message properties from Kafka headers."""
|
|
66
|
+
headers = self._msg.headers() or []
|
|
67
|
+
return {
|
|
68
|
+
k: v.decode('utf-8') if isinstance(v, bytes) else v
|
|
69
|
+
for k, v in headers
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class KafkaBackendProducer:
|
|
74
|
+
"""Publishes messages to a Kafka topic.
|
|
75
|
+
|
|
76
|
+
confluent-kafka Producer is thread-safe, so a single instance
|
|
77
|
+
can be shared across threads.
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
def __init__(self, bootstrap_servers, topic_name, durable):
|
|
81
|
+
self._topic_name = topic_name
|
|
82
|
+
self._durable = durable
|
|
83
|
+
self._producer = KafkaProducer({
|
|
84
|
+
'bootstrap.servers': bootstrap_servers,
|
|
85
|
+
'acks': 'all' if durable else '1',
|
|
86
|
+
'message.max.bytes': 10485760,
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
def send(self, message: Any, properties: dict = {}) -> None:
|
|
90
|
+
data_dict = dataclass_to_dict(message)
|
|
91
|
+
json_data = json.dumps(data_dict).encode('utf-8')
|
|
92
|
+
|
|
93
|
+
headers = [
|
|
94
|
+
(k, str(v).encode('utf-8'))
|
|
95
|
+
for k, v in properties.items()
|
|
96
|
+
] if properties else None
|
|
97
|
+
|
|
98
|
+
self._delivery_error = None
|
|
99
|
+
|
|
100
|
+
def _on_delivery(err, msg):
|
|
101
|
+
if err:
|
|
102
|
+
self._delivery_error = err
|
|
103
|
+
|
|
104
|
+
self._producer.produce(
|
|
105
|
+
topic=self._topic_name,
|
|
106
|
+
value=json_data,
|
|
107
|
+
headers=headers,
|
|
108
|
+
on_delivery=_on_delivery,
|
|
109
|
+
)
|
|
110
|
+
self._producer.flush()
|
|
111
|
+
|
|
112
|
+
if self._delivery_error:
|
|
113
|
+
raise KafkaException(self._delivery_error)
|
|
114
|
+
|
|
115
|
+
def flush(self) -> None:
|
|
116
|
+
self._producer.flush()
|
|
117
|
+
|
|
118
|
+
def close(self) -> None:
|
|
119
|
+
self._producer.flush()
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class KafkaBackendConsumer:
|
|
123
|
+
"""Consumes from a Kafka topic using a consumer group.
|
|
124
|
+
|
|
125
|
+
Uses confluent-kafka Consumer.poll() for message delivery.
|
|
126
|
+
Not thread-safe — each instance must be used from a single thread,
|
|
127
|
+
which matches the ThreadPoolExecutor pattern in consumer.py.
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
def __init__(self, bootstrap_servers, topic_name, group_id,
|
|
131
|
+
schema_cls, auto_offset_reset='latest'):
|
|
132
|
+
self._bootstrap_servers = bootstrap_servers
|
|
133
|
+
self._topic_name = topic_name
|
|
134
|
+
self._group_id = group_id
|
|
135
|
+
self._schema_cls = schema_cls
|
|
136
|
+
self._auto_offset_reset = auto_offset_reset
|
|
137
|
+
self._consumer = None
|
|
138
|
+
|
|
139
|
+
def _connect(self):
|
|
140
|
+
import time
|
|
141
|
+
t0 = time.monotonic()
|
|
142
|
+
|
|
143
|
+
def _on_assign(consumer, partitions):
|
|
144
|
+
elapsed = time.monotonic() - t0
|
|
145
|
+
logger.info(
|
|
146
|
+
f"Partition assignment for {self._topic_name}: "
|
|
147
|
+
f"{[p.partition for p in partitions]} "
|
|
148
|
+
f"after {elapsed:.1f}s"
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
def _on_revoke(consumer, partitions):
|
|
152
|
+
logger.info(
|
|
153
|
+
f"Partition revoke for {self._topic_name}: "
|
|
154
|
+
f"{[p.partition for p in partitions]}"
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
self._consumer = KafkaConsumer({
|
|
158
|
+
'bootstrap.servers': self._bootstrap_servers,
|
|
159
|
+
'group.id': self._group_id,
|
|
160
|
+
'auto.offset.reset': self._auto_offset_reset,
|
|
161
|
+
'enable.auto.commit': False,
|
|
162
|
+
'fetch.message.max.bytes': 10485760,
|
|
163
|
+
# Tighten group coordination timeouts for fast
|
|
164
|
+
# group join on single-member groups.
|
|
165
|
+
'session.timeout.ms': 6000,
|
|
166
|
+
'heartbeat.interval.ms': 1000,
|
|
167
|
+
})
|
|
168
|
+
self._consumer.subscribe(
|
|
169
|
+
[self._topic_name],
|
|
170
|
+
on_assign=_on_assign,
|
|
171
|
+
on_revoke=_on_revoke,
|
|
172
|
+
)
|
|
173
|
+
logger.info(
|
|
174
|
+
f"Kafka consumer subscribed: topic={self._topic_name}, "
|
|
175
|
+
f"group={self._group_id}"
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
def _is_alive(self):
|
|
179
|
+
return self._consumer is not None
|
|
180
|
+
|
|
181
|
+
def ensure_connected(self) -> None:
|
|
182
|
+
"""Eagerly connect and subscribe.
|
|
183
|
+
|
|
184
|
+
For response/notify consumers this must be called before the
|
|
185
|
+
corresponding request is published, so that the consumer is
|
|
186
|
+
assigned a partition and will see the response message.
|
|
187
|
+
"""
|
|
188
|
+
if not self._is_alive():
|
|
189
|
+
self._connect()
|
|
190
|
+
|
|
191
|
+
# Kick off group join. With auto.offset.reset=earliest
|
|
192
|
+
# on response/notify consumers, any messages published
|
|
193
|
+
# before assignment completes will be picked up once
|
|
194
|
+
# the consumer starts polling in receive().
|
|
195
|
+
self._consumer.poll(timeout=0.5)
|
|
196
|
+
|
|
197
|
+
def receive(self, timeout_millis: int = 2000) -> Message:
|
|
198
|
+
"""Receive a message. Raises TimeoutError if none available."""
|
|
199
|
+
if not self._is_alive():
|
|
200
|
+
self._connect()
|
|
201
|
+
|
|
202
|
+
timeout_seconds = timeout_millis / 1000.0
|
|
203
|
+
msg = self._consumer.poll(timeout=timeout_seconds)
|
|
204
|
+
|
|
205
|
+
if msg is None:
|
|
206
|
+
raise TimeoutError("No message received within timeout")
|
|
207
|
+
|
|
208
|
+
if msg.error():
|
|
209
|
+
error = msg.error()
|
|
210
|
+
if error.code() == KafkaError._PARTITION_EOF:
|
|
211
|
+
raise TimeoutError("End of partition reached")
|
|
212
|
+
if error.code() == KafkaError.UNKNOWN_TOPIC_OR_PART:
|
|
213
|
+
raise TimeoutError("Topic not yet available")
|
|
214
|
+
raise KafkaException(error)
|
|
215
|
+
|
|
216
|
+
return KafkaMessage(msg, self._schema_cls)
|
|
217
|
+
|
|
218
|
+
def acknowledge(self, message: Message) -> None:
|
|
219
|
+
"""Commit the message's offset (next offset to read)."""
|
|
220
|
+
if isinstance(message, KafkaMessage) and message._msg:
|
|
221
|
+
tp = TopicPartition(
|
|
222
|
+
message._msg.topic(),
|
|
223
|
+
message._msg.partition(),
|
|
224
|
+
message._msg.offset() + 1,
|
|
225
|
+
)
|
|
226
|
+
self._consumer.commit(offsets=[tp], asynchronous=False)
|
|
227
|
+
|
|
228
|
+
def negative_acknowledge(self, message: Message) -> None:
|
|
229
|
+
"""Seek back to the message's offset for redelivery."""
|
|
230
|
+
if isinstance(message, KafkaMessage) and message._msg:
|
|
231
|
+
tp = TopicPartition(
|
|
232
|
+
message._msg.topic(),
|
|
233
|
+
message._msg.partition(),
|
|
234
|
+
message._msg.offset(),
|
|
235
|
+
)
|
|
236
|
+
self._consumer.seek(tp)
|
|
237
|
+
|
|
238
|
+
def unsubscribe(self) -> None:
|
|
239
|
+
if self._consumer:
|
|
240
|
+
try:
|
|
241
|
+
self._consumer.unsubscribe()
|
|
242
|
+
except Exception:
|
|
243
|
+
pass
|
|
244
|
+
|
|
245
|
+
def close(self) -> None:
|
|
246
|
+
if self._consumer:
|
|
247
|
+
try:
|
|
248
|
+
self._consumer.close()
|
|
249
|
+
except Exception:
|
|
250
|
+
pass
|
|
251
|
+
self._consumer = None
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
class KafkaBackend:
|
|
255
|
+
"""Kafka pub/sub backend using one topic per logical topic."""
|
|
256
|
+
|
|
257
|
+
def __init__(self, bootstrap_servers='localhost:9092',
|
|
258
|
+
security_protocol='PLAINTEXT',
|
|
259
|
+
sasl_mechanism=None,
|
|
260
|
+
sasl_username=None,
|
|
261
|
+
sasl_password=None):
|
|
262
|
+
self._bootstrap_servers = bootstrap_servers
|
|
263
|
+
|
|
264
|
+
# AdminClient config
|
|
265
|
+
self._admin_config = {
|
|
266
|
+
'bootstrap.servers': bootstrap_servers,
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if security_protocol != 'PLAINTEXT':
|
|
270
|
+
self._admin_config['security.protocol'] = security_protocol
|
|
271
|
+
if sasl_mechanism:
|
|
272
|
+
self._admin_config['sasl.mechanism'] = sasl_mechanism
|
|
273
|
+
if sasl_username:
|
|
274
|
+
self._admin_config['sasl.username'] = sasl_username
|
|
275
|
+
if sasl_password:
|
|
276
|
+
self._admin_config['sasl.password'] = sasl_password
|
|
277
|
+
|
|
278
|
+
# Topics are created with 1 partition, so only 1 consumer
|
|
279
|
+
# per group can be active. Extra consumers cause rebalance
|
|
280
|
+
# storms that block message delivery.
|
|
281
|
+
self.max_consumer_concurrency = 1
|
|
282
|
+
|
|
283
|
+
logger.info(
|
|
284
|
+
f"Kafka backend: {bootstrap_servers} "
|
|
285
|
+
f"protocol={security_protocol}"
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
def _parse_topic(self, topic_id: str) -> tuple[str, str, bool]:
|
|
289
|
+
"""
|
|
290
|
+
Parse topic identifier into Kafka topic name, class, and durability.
|
|
291
|
+
|
|
292
|
+
Format: class:topicspace:topic
|
|
293
|
+
Returns: (topic_name, class, durable)
|
|
294
|
+
"""
|
|
295
|
+
if ':' not in topic_id:
|
|
296
|
+
return f'tg.flow.{topic_id}', 'flow', True
|
|
297
|
+
|
|
298
|
+
parts = topic_id.split(':', 2)
|
|
299
|
+
if len(parts) != 3:
|
|
300
|
+
raise ValueError(
|
|
301
|
+
f"Invalid topic format: {topic_id}, "
|
|
302
|
+
f"expected class:topicspace:topic"
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
cls, topicspace, topic = parts
|
|
306
|
+
|
|
307
|
+
if cls == 'flow':
|
|
308
|
+
durable = True
|
|
309
|
+
elif cls in ('request', 'response', 'notify'):
|
|
310
|
+
durable = False
|
|
311
|
+
else:
|
|
312
|
+
raise ValueError(
|
|
313
|
+
f"Invalid topic class: {cls}, "
|
|
314
|
+
f"expected flow, request, response, or notify"
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
# Replace any remaining colons — flow topics can have
|
|
318
|
+
# extra segments (e.g. flow:tg:document-load:default)
|
|
319
|
+
# and Kafka rejects colons in topic names.
|
|
320
|
+
topic_name = f"{topicspace}.{cls}.{topic}".replace(':', '.')
|
|
321
|
+
|
|
322
|
+
return topic_name, cls, durable
|
|
323
|
+
|
|
324
|
+
def _retention_ms(self, cls):
|
|
325
|
+
"""Return retention.ms for a topic class."""
|
|
326
|
+
if cls == 'flow':
|
|
327
|
+
return LONG_RETENTION_MS
|
|
328
|
+
return SHORT_RETENTION_MS
|
|
329
|
+
|
|
330
|
+
def create_producer(self, topic: str, schema: type,
|
|
331
|
+
**options) -> BackendProducer:
|
|
332
|
+
topic_name, cls, durable = self._parse_topic(topic)
|
|
333
|
+
logger.debug(f"Creating producer: topic={topic_name}")
|
|
334
|
+
return KafkaBackendProducer(
|
|
335
|
+
self._bootstrap_servers, topic_name, durable,
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
def create_consumer(self, topic: str, subscription: str, schema: type,
|
|
339
|
+
initial_position: str = 'latest',
|
|
340
|
+
**options) -> BackendConsumer:
|
|
341
|
+
"""Create a consumer subscribed to a Kafka topic.
|
|
342
|
+
|
|
343
|
+
Behaviour is determined by the topic's class prefix:
|
|
344
|
+
- flow: named consumer group, competing consumers
|
|
345
|
+
- request: named consumer group, competing consumers
|
|
346
|
+
- response: unique consumer group per instance
|
|
347
|
+
- notify: unique consumer group per instance
|
|
348
|
+
"""
|
|
349
|
+
topic_name, cls, durable = self._parse_topic(topic)
|
|
350
|
+
|
|
351
|
+
if cls in ('response', 'notify'):
|
|
352
|
+
# Per-subscriber: unique group so every instance sees
|
|
353
|
+
# every message. Filter by correlation ID happens at
|
|
354
|
+
# the Subscriber layer above.
|
|
355
|
+
# Use 'earliest' so that responses published before
|
|
356
|
+
# partition assignment completes are not missed.
|
|
357
|
+
# Each group is unique (UUID) with no committed offsets,
|
|
358
|
+
# so 'earliest' reads from the start of the topic.
|
|
359
|
+
# The correlation ID filter discards non-matching messages.
|
|
360
|
+
group_id = f"{subscription}-{uuid.uuid4()}"
|
|
361
|
+
auto_offset_reset = 'earliest'
|
|
362
|
+
else:
|
|
363
|
+
# Shared: named group, competing consumers
|
|
364
|
+
group_id = subscription
|
|
365
|
+
auto_offset_reset = (
|
|
366
|
+
'earliest' if initial_position == 'earliest'
|
|
367
|
+
else 'latest'
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
logger.debug(
|
|
371
|
+
f"Creating consumer: topic={topic_name}, "
|
|
372
|
+
f"group={group_id}, cls={cls}"
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
return KafkaBackendConsumer(
|
|
376
|
+
self._bootstrap_servers, topic_name, group_id,
|
|
377
|
+
schema, auto_offset_reset,
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
def _create_topic_sync(self, topic_name, retention_ms):
|
|
381
|
+
"""Blocking topic creation via AdminClient."""
|
|
382
|
+
admin = AdminClient(self._admin_config)
|
|
383
|
+
new_topic = NewTopic(
|
|
384
|
+
topic_name,
|
|
385
|
+
num_partitions=1,
|
|
386
|
+
replication_factor=1,
|
|
387
|
+
config={
|
|
388
|
+
'retention.ms': str(retention_ms),
|
|
389
|
+
},
|
|
390
|
+
)
|
|
391
|
+
fs = admin.create_topics([new_topic])
|
|
392
|
+
for name, f in fs.items():
|
|
393
|
+
try:
|
|
394
|
+
f.result()
|
|
395
|
+
logger.info(f"Created topic: {name}")
|
|
396
|
+
except KafkaException as e:
|
|
397
|
+
# Topic already exists — idempotent
|
|
398
|
+
if e.args[0].code() == KafkaError.TOPIC_ALREADY_EXISTS:
|
|
399
|
+
logger.debug(f"Topic already exists: {name}")
|
|
400
|
+
else:
|
|
401
|
+
raise
|
|
402
|
+
|
|
403
|
+
async def create_topic(self, topic: str) -> None:
|
|
404
|
+
"""Create a Kafka topic with appropriate retention."""
|
|
405
|
+
topic_name, cls, durable = self._parse_topic(topic)
|
|
406
|
+
retention_ms = self._retention_ms(cls)
|
|
407
|
+
await asyncio.to_thread(
|
|
408
|
+
self._create_topic_sync, topic_name, retention_ms,
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
def _delete_topic_sync(self, topic_name):
|
|
412
|
+
"""Blocking topic deletion via AdminClient."""
|
|
413
|
+
admin = AdminClient(self._admin_config)
|
|
414
|
+
fs = admin.delete_topics([topic_name])
|
|
415
|
+
for name, f in fs.items():
|
|
416
|
+
try:
|
|
417
|
+
f.result()
|
|
418
|
+
logger.info(f"Deleted topic: {name}")
|
|
419
|
+
except KafkaException as e:
|
|
420
|
+
# Topic doesn't exist — idempotent
|
|
421
|
+
if e.args[0].code() == KafkaError.UNKNOWN_TOPIC_OR_PART:
|
|
422
|
+
logger.debug(f"Topic not found: {name}")
|
|
423
|
+
else:
|
|
424
|
+
raise
|
|
425
|
+
except Exception as e:
|
|
426
|
+
logger.debug(f"Topic delete for {name}: {e}")
|
|
427
|
+
|
|
428
|
+
async def delete_topic(self, topic: str) -> None:
|
|
429
|
+
"""Delete a Kafka topic."""
|
|
430
|
+
topic_name, cls, durable = self._parse_topic(topic)
|
|
431
|
+
await asyncio.to_thread(self._delete_topic_sync, topic_name)
|
|
432
|
+
|
|
433
|
+
def _topic_exists_sync(self, topic_name):
|
|
434
|
+
"""Blocking topic existence check via AdminClient."""
|
|
435
|
+
admin = AdminClient(self._admin_config)
|
|
436
|
+
metadata = admin.list_topics(timeout=10)
|
|
437
|
+
return topic_name in metadata.topics
|
|
438
|
+
|
|
439
|
+
async def topic_exists(self, topic: str) -> bool:
|
|
440
|
+
"""Check whether a Kafka topic exists."""
|
|
441
|
+
topic_name, cls, durable = self._parse_topic(topic)
|
|
442
|
+
return await asyncio.to_thread(
|
|
443
|
+
self._topic_exists_sync, topic_name,
|
|
444
|
+
)
|
|
445
|
+
|
|
446
|
+
async def ensure_topic(self, topic: str) -> None:
|
|
447
|
+
"""Ensure a topic exists, creating it if necessary."""
|
|
448
|
+
if not await self.topic_exists(topic):
|
|
449
|
+
await self.create_topic(topic)
|
|
450
|
+
|
|
451
|
+
def close(self) -> None:
|
|
452
|
+
pass
|
|
@@ -17,6 +17,12 @@ DEFAULT_RABBITMQ_USERNAME = os.getenv("RABBITMQ_USERNAME", 'guest')
|
|
|
17
17
|
DEFAULT_RABBITMQ_PASSWORD = os.getenv("RABBITMQ_PASSWORD", 'guest')
|
|
18
18
|
DEFAULT_RABBITMQ_VHOST = os.getenv("RABBITMQ_VHOST", '/')
|
|
19
19
|
|
|
20
|
+
DEFAULT_KAFKA_BOOTSTRAP = os.getenv("KAFKA_BOOTSTRAP_SERVERS", 'kafka:9092')
|
|
21
|
+
DEFAULT_KAFKA_PROTOCOL = os.getenv("KAFKA_SECURITY_PROTOCOL", 'PLAINTEXT')
|
|
22
|
+
DEFAULT_KAFKA_SASL_MECHANISM = os.getenv("KAFKA_SASL_MECHANISM", None)
|
|
23
|
+
DEFAULT_KAFKA_SASL_USERNAME = os.getenv("KAFKA_SASL_USERNAME", None)
|
|
24
|
+
DEFAULT_KAFKA_SASL_PASSWORD = os.getenv("KAFKA_SASL_PASSWORD", None)
|
|
25
|
+
|
|
20
26
|
|
|
21
27
|
def get_pubsub(**config: Any) -> Any:
|
|
22
28
|
"""
|
|
@@ -47,6 +53,25 @@ def get_pubsub(**config: Any) -> Any:
|
|
|
47
53
|
password=config.get('rabbitmq_password', DEFAULT_RABBITMQ_PASSWORD),
|
|
48
54
|
vhost=config.get('rabbitmq_vhost', DEFAULT_RABBITMQ_VHOST),
|
|
49
55
|
)
|
|
56
|
+
elif backend_type == 'kafka':
|
|
57
|
+
from .kafka_backend import KafkaBackend
|
|
58
|
+
return KafkaBackend(
|
|
59
|
+
bootstrap_servers=config.get(
|
|
60
|
+
'kafka_bootstrap_servers', DEFAULT_KAFKA_BOOTSTRAP,
|
|
61
|
+
),
|
|
62
|
+
security_protocol=config.get(
|
|
63
|
+
'kafka_security_protocol', DEFAULT_KAFKA_PROTOCOL,
|
|
64
|
+
),
|
|
65
|
+
sasl_mechanism=config.get(
|
|
66
|
+
'kafka_sasl_mechanism', DEFAULT_KAFKA_SASL_MECHANISM,
|
|
67
|
+
),
|
|
68
|
+
sasl_username=config.get(
|
|
69
|
+
'kafka_sasl_username', DEFAULT_KAFKA_SASL_USERNAME,
|
|
70
|
+
),
|
|
71
|
+
sasl_password=config.get(
|
|
72
|
+
'kafka_sasl_password', DEFAULT_KAFKA_SASL_PASSWORD,
|
|
73
|
+
),
|
|
74
|
+
)
|
|
50
75
|
else:
|
|
51
76
|
raise ValueError(f"Unknown pub/sub backend: {backend_type}")
|
|
52
77
|
|
|
@@ -65,6 +90,7 @@ def add_pubsub_args(parser: ArgumentParser, standalone: bool = False) -> None:
|
|
|
65
90
|
pulsar_host = STANDALONE_PULSAR_HOST if standalone else DEFAULT_PULSAR_HOST
|
|
66
91
|
pulsar_listener = 'localhost' if standalone else None
|
|
67
92
|
rabbitmq_host = 'localhost' if standalone else DEFAULT_RABBITMQ_HOST
|
|
93
|
+
kafka_bootstrap = 'localhost:9092' if standalone else DEFAULT_KAFKA_BOOTSTRAP
|
|
68
94
|
|
|
69
95
|
parser.add_argument(
|
|
70
96
|
'--pubsub-backend',
|
|
@@ -122,3 +148,34 @@ def add_pubsub_args(parser: ArgumentParser, standalone: bool = False) -> None:
|
|
|
122
148
|
default=DEFAULT_RABBITMQ_VHOST,
|
|
123
149
|
help=f'RabbitMQ vhost (default: {DEFAULT_RABBITMQ_VHOST})',
|
|
124
150
|
)
|
|
151
|
+
|
|
152
|
+
# Kafka options
|
|
153
|
+
parser.add_argument(
|
|
154
|
+
'--kafka-bootstrap-servers',
|
|
155
|
+
default=kafka_bootstrap,
|
|
156
|
+
help=f'Kafka bootstrap servers (default: {kafka_bootstrap})',
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
parser.add_argument(
|
|
160
|
+
'--kafka-security-protocol',
|
|
161
|
+
default=DEFAULT_KAFKA_PROTOCOL,
|
|
162
|
+
help=f'Kafka security protocol (default: {DEFAULT_KAFKA_PROTOCOL})',
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
parser.add_argument(
|
|
166
|
+
'--kafka-sasl-mechanism',
|
|
167
|
+
default=DEFAULT_KAFKA_SASL_MECHANISM,
|
|
168
|
+
help='Kafka SASL mechanism',
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
parser.add_argument(
|
|
172
|
+
'--kafka-sasl-username',
|
|
173
|
+
default=DEFAULT_KAFKA_SASL_USERNAME,
|
|
174
|
+
help='Kafka SASL username',
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
parser.add_argument(
|
|
178
|
+
'--kafka-sasl-password',
|
|
179
|
+
default=DEFAULT_KAFKA_SASL_PASSWORD,
|
|
180
|
+
help='Kafka SASL password',
|
|
181
|
+
)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.3.16"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: trustgraph-base
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.16
|
|
4
4
|
Summary: TrustGraph provides a means to run a pipeline of flexible AI processing components in a flexible means to achieve a processing pipeline.
|
|
5
5
|
Author-email: "trustgraph.ai" <security@trustgraph.ai>
|
|
6
6
|
Project-URL: Homepage, https://github.com/trustgraph-ai/trustgraph
|
|
@@ -13,6 +13,7 @@ Requires-Dist: prometheus-client
|
|
|
13
13
|
Requires-Dist: requests
|
|
14
14
|
Requires-Dist: python-logging-loki
|
|
15
15
|
Requires-Dist: pika
|
|
16
|
+
Requires-Dist: confluent-kafka
|
|
16
17
|
Requires-Dist: pyyaml
|
|
17
18
|
|
|
18
19
|
See https://trustgraph.ai/
|
|
@@ -44,6 +44,7 @@ trustgraph/base/graph_embeddings_client.py
|
|
|
44
44
|
trustgraph/base/graph_embeddings_query_service.py
|
|
45
45
|
trustgraph/base/graph_embeddings_store_service.py
|
|
46
46
|
trustgraph/base/graph_rag_client.py
|
|
47
|
+
trustgraph/base/kafka_backend.py
|
|
47
48
|
trustgraph/base/librarian_client.py
|
|
48
49
|
trustgraph/base/llm_service.py
|
|
49
50
|
trustgraph/base/logging.py
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "2.3.14"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|