trustgraph-base 0.22.5__tar.gz → 0.22.7__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-0.22.5 → trustgraph-base-0.22.7}/PKG-INFO +2 -2
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph/api/api.py +230 -0
- trustgraph-base-0.22.7/trustgraph/base/__init__.py +31 -0
- trustgraph-base-0.22.7/trustgraph/base/agent_client.py +39 -0
- trustgraph-base-0.22.7/trustgraph/base/agent_service.py +100 -0
- trustgraph-base-0.22.7/trustgraph/base/async_processor.py +257 -0
- trustgraph-base-0.22.7/trustgraph/base/consumer.py +197 -0
- trustgraph-base-0.22.7/trustgraph/base/consumer_spec.py +36 -0
- trustgraph-base-0.22.7/trustgraph/base/document_embeddings_client.py +38 -0
- trustgraph-base-0.22.7/trustgraph/base/document_embeddings_query_service.py +84 -0
- trustgraph-base-0.22.7/trustgraph/base/document_embeddings_store_service.py +50 -0
- trustgraph-base-0.22.7/trustgraph/base/embeddings_client.py +31 -0
- trustgraph-base-0.22.7/trustgraph/base/embeddings_service.py +90 -0
- trustgraph-base-0.22.7/trustgraph/base/flow.py +32 -0
- trustgraph-base-0.22.7/trustgraph/base/flow_processor.py +115 -0
- trustgraph-base-0.22.7/trustgraph/base/graph_embeddings_client.py +45 -0
- trustgraph-base-0.22.7/trustgraph/base/graph_embeddings_query_service.py +84 -0
- trustgraph-base-0.22.7/trustgraph/base/graph_embeddings_store_service.py +50 -0
- trustgraph-base-0.22.7/trustgraph/base/graph_rag_client.py +33 -0
- trustgraph-base-0.22.7/trustgraph/base/llm_service.py +114 -0
- trustgraph-base-0.22.7/trustgraph/base/metrics.py +136 -0
- trustgraph-base-0.22.7/trustgraph/base/producer.py +69 -0
- trustgraph-base-0.22.7/trustgraph/base/producer_spec.py +25 -0
- trustgraph-base-0.22.7/trustgraph/base/prompt_client.py +93 -0
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph/base/publisher.py +23 -19
- trustgraph-base-0.22.7/trustgraph/base/pubsub.py +80 -0
- trustgraph-base-0.22.7/trustgraph/base/request_response_spec.py +141 -0
- trustgraph-base-0.22.7/trustgraph/base/setting_spec.py +19 -0
- trustgraph-base-0.22.7/trustgraph/base/spec.py +4 -0
- trustgraph-base-0.22.7/trustgraph/base/subscriber.py +156 -0
- trustgraph-base-0.22.7/trustgraph/base/subscriber_spec.py +30 -0
- trustgraph-base-0.22.7/trustgraph/base/text_completion_client.py +30 -0
- trustgraph-base-0.22.7/trustgraph/base/triples_client.py +61 -0
- trustgraph-base-0.22.7/trustgraph/base/triples_query_service.py +82 -0
- trustgraph-base-0.22.7/trustgraph/base/triples_store_service.py +47 -0
- trustgraph-base-0.22.7/trustgraph/base_version.py +1 -0
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph/schema/__init__.py +1 -1
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph/schema/agent.py +0 -7
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph/schema/config.py +1 -1
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph/schema/documents.py +0 -15
- trustgraph-base-0.22.7/trustgraph/schema/flows.py +66 -0
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph/schema/graph.py +0 -19
- trustgraph-base-0.22.7/trustgraph/schema/lookup.py +21 -0
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph/schema/models.py +0 -13
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph/schema/object.py +1 -3
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph/schema/prompt.py +0 -7
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph/schema/retrieval.py +0 -13
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph_base.egg-info/PKG-INFO +2 -2
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph_base.egg-info/SOURCES.txt +29 -2
- trustgraph-base-0.22.5/trustgraph/base/__init__.py +0 -8
- trustgraph-base-0.22.5/trustgraph/base/base_processor.py +0 -210
- trustgraph-base-0.22.5/trustgraph/base/consumer.py +0 -173
- trustgraph-base-0.22.5/trustgraph/base/consumer_producer.py +0 -62
- trustgraph-base-0.22.5/trustgraph/base/producer.py +0 -56
- trustgraph-base-0.22.5/trustgraph/base/subscriber.py +0 -114
- trustgraph-base-0.22.5/trustgraph/base_version.py +0 -1
- trustgraph-base-0.22.5/trustgraph/schema/lookup.py +0 -42
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/README.md +0 -0
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/setup.cfg +0 -0
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/setup.py +0 -0
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph/api/__init__.py +0 -0
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph/clients/__init__.py +0 -0
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph/clients/agent_client.py +0 -0
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph/clients/base.py +0 -0
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph/clients/config_client.py +0 -0
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph/clients/document_embeddings_client.py +0 -0
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph/clients/document_rag_client.py +0 -0
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph/clients/embeddings_client.py +0 -0
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph/clients/graph_embeddings_client.py +0 -0
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph/clients/graph_rag_client.py +0 -0
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph/clients/llm_client.py +0 -0
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph/clients/prompt_client.py +0 -0
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph/clients/triples_query_client.py +0 -0
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph/exceptions.py +0 -0
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph/knowledge/__init__.py +0 -0
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph/knowledge/defs.py +0 -0
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph/knowledge/document.py +0 -0
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph/knowledge/identifier.py +0 -0
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph/knowledge/organization.py +0 -0
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph/knowledge/publication.py +0 -0
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph/log_level.py +0 -0
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph/objects/__init__.py +0 -0
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph/objects/field.py +0 -0
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph/objects/object.py +0 -0
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph/rdf.py +0 -0
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph/schema/library.py +0 -0
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph/schema/metadata.py +0 -0
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph/schema/topic.py +0 -0
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph/schema/types.py +0 -0
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph_base.egg-info/dependency_links.txt +0 -0
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph_base.egg-info/requires.txt +0 -0
- {trustgraph-base-0.22.5 → trustgraph-base-0.22.7}/trustgraph_base.egg-info/top_level.txt +0 -0
@@ -1,9 +1,9 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: trustgraph-base
|
3
|
-
Version: 0.22.
|
3
|
+
Version: 0.22.7
|
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
|
Home-page: https://github.com/trustgraph-ai/trustgraph
|
6
|
-
Download-URL: https://github.com/trustgraph-ai/trustgraph/archive/refs/tags/v0.22.
|
6
|
+
Download-URL: https://github.com/trustgraph-ai/trustgraph/archive/refs/tags/v0.22.7.tar.gz
|
7
7
|
Author: trustgraph.ai
|
8
8
|
Author-email: security@trustgraph.ai
|
9
9
|
Classifier: Programming Language :: Python :: 3
|
@@ -562,3 +562,233 @@ class Api:
|
|
562
562
|
except:
|
563
563
|
raise ProtocolException(f"Response not formatted correctly")
|
564
564
|
|
565
|
+
def flow_list_classes(self):
|
566
|
+
|
567
|
+
# The input consists of system and prompt strings
|
568
|
+
input = {
|
569
|
+
"operation": "list-classes",
|
570
|
+
}
|
571
|
+
|
572
|
+
url = f"{self.url}flow"
|
573
|
+
|
574
|
+
# Invoke the API, input is passed as JSON
|
575
|
+
resp = requests.post(url, json=input)
|
576
|
+
|
577
|
+
# Should be a 200 status code
|
578
|
+
if resp.status_code != 200:
|
579
|
+
raise ProtocolException(f"Status code {resp.status_code}")
|
580
|
+
|
581
|
+
try:
|
582
|
+
# Parse the response as JSON
|
583
|
+
object = resp.json()
|
584
|
+
except:
|
585
|
+
raise ProtocolException(f"Expected JSON response")
|
586
|
+
|
587
|
+
self.check_error(object)
|
588
|
+
|
589
|
+
try:
|
590
|
+
return object["class-names"]
|
591
|
+
except:
|
592
|
+
raise ProtocolException(f"Response not formatted correctly")
|
593
|
+
|
594
|
+
def flow_get_class(self, class_name):
|
595
|
+
|
596
|
+
# The input consists of system and prompt strings
|
597
|
+
input = {
|
598
|
+
"operation": "get-class",
|
599
|
+
"class-name": class_name,
|
600
|
+
}
|
601
|
+
|
602
|
+
url = f"{self.url}flow"
|
603
|
+
|
604
|
+
# Invoke the API, input is passed as JSON
|
605
|
+
resp = requests.post(url, json=input)
|
606
|
+
|
607
|
+
# Should be a 200 status code
|
608
|
+
if resp.status_code != 200:
|
609
|
+
raise ProtocolException(f"Status code {resp.status_code}")
|
610
|
+
|
611
|
+
try:
|
612
|
+
# Parse the response as JSON
|
613
|
+
object = resp.json()
|
614
|
+
except:
|
615
|
+
raise ProtocolException(f"Expected JSON response")
|
616
|
+
|
617
|
+
self.check_error(object)
|
618
|
+
|
619
|
+
try:
|
620
|
+
return json.loads(object["class-definition"])
|
621
|
+
except Exception as e:
|
622
|
+
print(e)
|
623
|
+
raise ProtocolException(f"Response not formatted correctly")
|
624
|
+
|
625
|
+
def flow_put_class(self, class_name, definition):
|
626
|
+
|
627
|
+
# The input consists of system and prompt strings
|
628
|
+
input = {
|
629
|
+
"operation": "put-class",
|
630
|
+
"class-name": class_name,
|
631
|
+
"class-definition": json.dumps(definition),
|
632
|
+
}
|
633
|
+
|
634
|
+
url = f"{self.url}flow"
|
635
|
+
|
636
|
+
# Invoke the API, input is passed as JSON
|
637
|
+
resp = requests.post(url, json=input)
|
638
|
+
|
639
|
+
# Should be a 200 status code
|
640
|
+
if resp.status_code != 200:
|
641
|
+
raise ProtocolException(f"Status code {resp.status_code}")
|
642
|
+
|
643
|
+
try:
|
644
|
+
# Parse the response as JSON
|
645
|
+
object = resp.json()
|
646
|
+
except:
|
647
|
+
raise ProtocolException(f"Expected JSON response")
|
648
|
+
|
649
|
+
self.check_error(object)
|
650
|
+
|
651
|
+
return
|
652
|
+
|
653
|
+
def flow_delete_class(self, class_name):
|
654
|
+
|
655
|
+
# The input consists of system and prompt strings
|
656
|
+
input = {
|
657
|
+
"operation": "delete-class",
|
658
|
+
"class-name": class_name,
|
659
|
+
}
|
660
|
+
|
661
|
+
url = f"{self.url}flow"
|
662
|
+
|
663
|
+
# Invoke the API, input is passed as JSON
|
664
|
+
resp = requests.post(url, json=input)
|
665
|
+
|
666
|
+
# Should be a 200 status code
|
667
|
+
if resp.status_code != 200:
|
668
|
+
raise ProtocolException(f"Status code {resp.status_code}")
|
669
|
+
|
670
|
+
try:
|
671
|
+
# Parse the response as JSON
|
672
|
+
object = resp.json()
|
673
|
+
except:
|
674
|
+
raise ProtocolException(f"Expected JSON response")
|
675
|
+
|
676
|
+
self.check_error(object)
|
677
|
+
|
678
|
+
return
|
679
|
+
|
680
|
+
def flow_list(self):
|
681
|
+
|
682
|
+
# The input consists of system and prompt strings
|
683
|
+
input = {
|
684
|
+
"operation": "list-flows",
|
685
|
+
}
|
686
|
+
|
687
|
+
url = f"{self.url}flow"
|
688
|
+
|
689
|
+
# Invoke the API, input is passed as JSON
|
690
|
+
resp = requests.post(url, json=input)
|
691
|
+
|
692
|
+
# Should be a 200 status code
|
693
|
+
if resp.status_code != 200:
|
694
|
+
raise ProtocolException(f"Status code {resp.status_code}")
|
695
|
+
|
696
|
+
try:
|
697
|
+
# Parse the response as JSON
|
698
|
+
object = resp.json()
|
699
|
+
except:
|
700
|
+
raise ProtocolException(f"Expected JSON response")
|
701
|
+
|
702
|
+
self.check_error(object)
|
703
|
+
|
704
|
+
try:
|
705
|
+
return object["flow-ids"]
|
706
|
+
except:
|
707
|
+
raise ProtocolException(f"Response not formatted correctly")
|
708
|
+
|
709
|
+
def flow_get(self, id):
|
710
|
+
|
711
|
+
# The input consists of system and prompt strings
|
712
|
+
input = {
|
713
|
+
"operation": "get-flow",
|
714
|
+
"flow-id": id,
|
715
|
+
}
|
716
|
+
|
717
|
+
url = f"{self.url}flow"
|
718
|
+
|
719
|
+
# Invoke the API, input is passed as JSON
|
720
|
+
resp = requests.post(url, json=input)
|
721
|
+
|
722
|
+
# Should be a 200 status code
|
723
|
+
if resp.status_code != 200:
|
724
|
+
raise ProtocolException(f"Status code {resp.status_code}")
|
725
|
+
|
726
|
+
try:
|
727
|
+
# Parse the response as JSON
|
728
|
+
object = resp.json()
|
729
|
+
except:
|
730
|
+
raise ProtocolException(f"Expected JSON response")
|
731
|
+
|
732
|
+
self.check_error(object)
|
733
|
+
|
734
|
+
try:
|
735
|
+
return json.loads(object["flow"])
|
736
|
+
except:
|
737
|
+
raise ProtocolException(f"Response not formatted correctly")
|
738
|
+
|
739
|
+
def flow_start(self, class_name, id, description):
|
740
|
+
|
741
|
+
# The input consists of system and prompt strings
|
742
|
+
input = {
|
743
|
+
"operation": "start-flow",
|
744
|
+
"flow-id": id,
|
745
|
+
"class-name": class_name,
|
746
|
+
"description": description,
|
747
|
+
}
|
748
|
+
|
749
|
+
url = f"{self.url}flow"
|
750
|
+
|
751
|
+
# Invoke the API, input is passed as JSON
|
752
|
+
resp = requests.post(url, json=input)
|
753
|
+
|
754
|
+
# Should be a 200 status code
|
755
|
+
if resp.status_code != 200:
|
756
|
+
raise ProtocolException(f"Status code {resp.status_code}")
|
757
|
+
|
758
|
+
try:
|
759
|
+
# Parse the response as JSON
|
760
|
+
object = resp.json()
|
761
|
+
except:
|
762
|
+
raise ProtocolException(f"Expected JSON response")
|
763
|
+
|
764
|
+
self.check_error(object)
|
765
|
+
|
766
|
+
return
|
767
|
+
|
768
|
+
def flow_stop(self, id):
|
769
|
+
|
770
|
+
# The input consists of system and prompt strings
|
771
|
+
input = {
|
772
|
+
"operation": "stop-flow",
|
773
|
+
"flow-id": id,
|
774
|
+
}
|
775
|
+
|
776
|
+
url = f"{self.url}flow"
|
777
|
+
|
778
|
+
# Invoke the API, input is passed as JSON
|
779
|
+
resp = requests.post(url, json=input)
|
780
|
+
|
781
|
+
# Should be a 200 status code
|
782
|
+
if resp.status_code != 200:
|
783
|
+
raise ProtocolException(f"Status code {resp.status_code}")
|
784
|
+
|
785
|
+
try:
|
786
|
+
# Parse the response as JSON
|
787
|
+
object = resp.json()
|
788
|
+
except:
|
789
|
+
raise ProtocolException(f"Expected JSON response")
|
790
|
+
|
791
|
+
self.check_error(object)
|
792
|
+
|
793
|
+
return
|
794
|
+
|
@@ -0,0 +1,31 @@
|
|
1
|
+
|
2
|
+
from . pubsub import PulsarClient
|
3
|
+
from . async_processor import AsyncProcessor
|
4
|
+
from . consumer import Consumer
|
5
|
+
from . producer import Producer
|
6
|
+
from . publisher import Publisher
|
7
|
+
from . subscriber import Subscriber
|
8
|
+
from . metrics import ProcessorMetrics, ConsumerMetrics, ProducerMetrics
|
9
|
+
from . flow_processor import FlowProcessor
|
10
|
+
from . consumer_spec import ConsumerSpec
|
11
|
+
from . setting_spec import SettingSpec
|
12
|
+
from . producer_spec import ProducerSpec
|
13
|
+
from . subscriber_spec import SubscriberSpec
|
14
|
+
from . request_response_spec import RequestResponseSpec
|
15
|
+
from . llm_service import LlmService, LlmResult
|
16
|
+
from . embeddings_service import EmbeddingsService
|
17
|
+
from . embeddings_client import EmbeddingsClientSpec
|
18
|
+
from . text_completion_client import TextCompletionClientSpec
|
19
|
+
from . prompt_client import PromptClientSpec
|
20
|
+
from . triples_store_service import TriplesStoreService
|
21
|
+
from . graph_embeddings_store_service import GraphEmbeddingsStoreService
|
22
|
+
from . document_embeddings_store_service import DocumentEmbeddingsStoreService
|
23
|
+
from . triples_query_service import TriplesQueryService
|
24
|
+
from . graph_embeddings_query_service import GraphEmbeddingsQueryService
|
25
|
+
from . document_embeddings_query_service import DocumentEmbeddingsQueryService
|
26
|
+
from . graph_embeddings_client import GraphEmbeddingsClientSpec
|
27
|
+
from . triples_client import TriplesClientSpec
|
28
|
+
from . document_embeddings_client import DocumentEmbeddingsClientSpec
|
29
|
+
from . agent_service import AgentService
|
30
|
+
from . graph_rag_client import GraphRagClientSpec
|
31
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
|
2
|
+
from . request_response_spec import RequestResponse, RequestResponseSpec
|
3
|
+
from .. schema import AgentRequest, AgentResponse
|
4
|
+
from .. knowledge import Uri, Literal
|
5
|
+
|
6
|
+
class AgentClient(RequestResponse):
|
7
|
+
async def request(self, recipient, question, plan=None, state=None,
|
8
|
+
history=[], timeout=300):
|
9
|
+
|
10
|
+
resp = await self.request(
|
11
|
+
AgentRequest(
|
12
|
+
question = question,
|
13
|
+
plan = plan,
|
14
|
+
state = state,
|
15
|
+
history = history,
|
16
|
+
),
|
17
|
+
recipient=recipient,
|
18
|
+
timeout=timeout,
|
19
|
+
)
|
20
|
+
|
21
|
+
print(resp, flush=True)
|
22
|
+
|
23
|
+
if resp.error:
|
24
|
+
raise RuntimeError(resp.error.message)
|
25
|
+
|
26
|
+
return resp
|
27
|
+
|
28
|
+
class GraphEmbeddingsClientSpec(RequestResponseSpec):
|
29
|
+
def __init__(
|
30
|
+
self, request_name, response_name,
|
31
|
+
):
|
32
|
+
super(GraphEmbeddingsClientSpec, self).__init__(
|
33
|
+
request_name = request_name,
|
34
|
+
request_schema = GraphEmbeddingsRequest,
|
35
|
+
response_name = response_name,
|
36
|
+
response_schema = GraphEmbeddingsResponse,
|
37
|
+
impl = GraphEmbeddingsClient,
|
38
|
+
)
|
39
|
+
|
@@ -0,0 +1,100 @@
|
|
1
|
+
|
2
|
+
"""
|
3
|
+
Agent manager service completion base class
|
4
|
+
"""
|
5
|
+
|
6
|
+
import time
|
7
|
+
from prometheus_client import Histogram
|
8
|
+
|
9
|
+
from .. schema import AgentRequest, AgentResponse, Error
|
10
|
+
from .. exceptions import TooManyRequests
|
11
|
+
from .. base import FlowProcessor, ConsumerSpec, ProducerSpec
|
12
|
+
|
13
|
+
default_ident = "agent-manager"
|
14
|
+
|
15
|
+
class AgentService(FlowProcessor):
|
16
|
+
|
17
|
+
def __init__(self, **params):
|
18
|
+
|
19
|
+
id = params.get("id")
|
20
|
+
|
21
|
+
super(AgentService, self).__init__(**params | { "id": id })
|
22
|
+
|
23
|
+
self.register_specification(
|
24
|
+
ConsumerSpec(
|
25
|
+
name = "request",
|
26
|
+
schema = AgentRequest,
|
27
|
+
handler = self.on_request
|
28
|
+
)
|
29
|
+
)
|
30
|
+
|
31
|
+
self.register_specification(
|
32
|
+
ProducerSpec(
|
33
|
+
name = "next",
|
34
|
+
schema = AgentRequest
|
35
|
+
)
|
36
|
+
)
|
37
|
+
|
38
|
+
self.register_specification(
|
39
|
+
ProducerSpec(
|
40
|
+
name = "response",
|
41
|
+
schema = AgentResponse
|
42
|
+
)
|
43
|
+
)
|
44
|
+
|
45
|
+
async def on_request(self, msg, consumer, flow):
|
46
|
+
|
47
|
+
try:
|
48
|
+
|
49
|
+
request = msg.value()
|
50
|
+
|
51
|
+
# Sender-produced ID
|
52
|
+
id = msg.properties()["id"]
|
53
|
+
|
54
|
+
async def respond(resp):
|
55
|
+
|
56
|
+
await flow("response").send(
|
57
|
+
resp,
|
58
|
+
properties={"id": id}
|
59
|
+
)
|
60
|
+
|
61
|
+
async def next(resp):
|
62
|
+
|
63
|
+
await flow("next").send(
|
64
|
+
resp,
|
65
|
+
properties={"id": id}
|
66
|
+
)
|
67
|
+
|
68
|
+
await self.agent_request(
|
69
|
+
request = request, respond = respond, next = next,
|
70
|
+
flow = flow
|
71
|
+
)
|
72
|
+
|
73
|
+
except TooManyRequests as e:
|
74
|
+
raise e
|
75
|
+
|
76
|
+
except Exception as e:
|
77
|
+
|
78
|
+
# Apart from rate limits, treat all exceptions as unrecoverable
|
79
|
+
print(f"on_request Exception: {e}")
|
80
|
+
|
81
|
+
print("Send error response...", flush=True)
|
82
|
+
|
83
|
+
await flow.producer["response"].send(
|
84
|
+
AgentResponse(
|
85
|
+
error=Error(
|
86
|
+
type = "agent-error",
|
87
|
+
message = str(e),
|
88
|
+
),
|
89
|
+
thought = None,
|
90
|
+
observation = None,
|
91
|
+
answer = None,
|
92
|
+
),
|
93
|
+
properties={"id": id}
|
94
|
+
)
|
95
|
+
|
96
|
+
@staticmethod
|
97
|
+
def add_args(parser):
|
98
|
+
|
99
|
+
FlowProcessor.add_args(parser)
|
100
|
+
|
@@ -0,0 +1,257 @@
|
|
1
|
+
|
2
|
+
# Base class for processors. Implements:
|
3
|
+
# - Pulsar client, subscribe and consume basic
|
4
|
+
# - the async startup logic
|
5
|
+
# - Initialising metrics
|
6
|
+
|
7
|
+
import asyncio
|
8
|
+
import argparse
|
9
|
+
import _pulsar
|
10
|
+
import time
|
11
|
+
import uuid
|
12
|
+
from prometheus_client import start_http_server, Info
|
13
|
+
|
14
|
+
from .. schema import ConfigPush, config_push_queue
|
15
|
+
from .. log_level import LogLevel
|
16
|
+
from .. exceptions import TooManyRequests
|
17
|
+
from . pubsub import PulsarClient
|
18
|
+
from . producer import Producer
|
19
|
+
from . consumer import Consumer
|
20
|
+
from . metrics import ProcessorMetrics, ConsumerMetrics
|
21
|
+
|
22
|
+
default_config_queue = config_push_queue
|
23
|
+
|
24
|
+
# Async processor
|
25
|
+
class AsyncProcessor:
|
26
|
+
|
27
|
+
def __init__(self, **params):
|
28
|
+
|
29
|
+
# Store the identity
|
30
|
+
self.id = params.get("id")
|
31
|
+
|
32
|
+
# Register a pulsar client
|
33
|
+
self.pulsar_client_object = PulsarClient(**params)
|
34
|
+
|
35
|
+
# Initialise metrics, records the parameters
|
36
|
+
ProcessorMetrics(processor = self.id).info({
|
37
|
+
k: str(params[k])
|
38
|
+
for k in params
|
39
|
+
if k != "id"
|
40
|
+
})
|
41
|
+
|
42
|
+
# The processor runs all activity in a taskgroup, it's mandatory
|
43
|
+
# that this is provded
|
44
|
+
self.taskgroup = params.get("taskgroup")
|
45
|
+
if self.taskgroup is None:
|
46
|
+
raise RuntimeError("Essential taskgroup missing")
|
47
|
+
|
48
|
+
# Get the configuration topic
|
49
|
+
self.config_push_queue = params.get(
|
50
|
+
"config_push_queue", default_config_queue
|
51
|
+
)
|
52
|
+
|
53
|
+
# This records registered configuration handlers
|
54
|
+
self.config_handlers = []
|
55
|
+
|
56
|
+
# Create a random ID for this subscription to the configuration
|
57
|
+
# service
|
58
|
+
config_subscriber_id = str(uuid.uuid4())
|
59
|
+
|
60
|
+
config_consumer_metrics = ConsumerMetrics(
|
61
|
+
processor = self.id, flow = None, name = "config",
|
62
|
+
)
|
63
|
+
|
64
|
+
# Subscribe to config queue
|
65
|
+
self.config_sub_task = Consumer(
|
66
|
+
|
67
|
+
taskgroup = self.taskgroup,
|
68
|
+
client = self.pulsar_client,
|
69
|
+
subscriber = config_subscriber_id,
|
70
|
+
flow = None,
|
71
|
+
|
72
|
+
topic = self.config_push_queue,
|
73
|
+
schema = ConfigPush,
|
74
|
+
|
75
|
+
handler = self.on_config_change,
|
76
|
+
|
77
|
+
metrics = config_consumer_metrics,
|
78
|
+
|
79
|
+
# This causes new subscriptions to view the entire history of
|
80
|
+
# configuration
|
81
|
+
start_of_messages = True
|
82
|
+
)
|
83
|
+
|
84
|
+
self.running = True
|
85
|
+
|
86
|
+
# This is called to start dynamic behaviour. An over-ride point for
|
87
|
+
# extra functionality
|
88
|
+
async def start(self):
|
89
|
+
await self.config_sub_task.start()
|
90
|
+
|
91
|
+
# This is called to stop all threads. An over-ride point for extra
|
92
|
+
# functionality
|
93
|
+
def stop(self):
|
94
|
+
self.pulsar_client.close()
|
95
|
+
self.running = False
|
96
|
+
|
97
|
+
# Returns the pulsar host
|
98
|
+
@property
|
99
|
+
def pulsar_host(self): return self.pulsar_client_object.pulsar_host
|
100
|
+
|
101
|
+
# Returns the pulsar client
|
102
|
+
@property
|
103
|
+
def pulsar_client(self): return self.pulsar_client_object.client
|
104
|
+
|
105
|
+
# Register a new event handler for configuration change
|
106
|
+
def register_config_handler(self, handler):
|
107
|
+
self.config_handlers.append(handler)
|
108
|
+
|
109
|
+
# Called when a new configuration message push occurs
|
110
|
+
async def on_config_change(self, message, consumer, flow):
|
111
|
+
|
112
|
+
# Get configuration data and version number
|
113
|
+
config = message.value().config
|
114
|
+
version = message.value().version
|
115
|
+
|
116
|
+
# Invoke message handlers
|
117
|
+
print("Config change event", config, version, flush=True)
|
118
|
+
for ch in self.config_handlers:
|
119
|
+
await ch(config, version)
|
120
|
+
|
121
|
+
# This is the 'main' body of the handler. It is a point to override
|
122
|
+
# if needed. By default does nothing. Processors are implemented
|
123
|
+
# by adding consumer/producer functionality so maybe nothing is needed
|
124
|
+
# in the run() body
|
125
|
+
async def run(self):
|
126
|
+
while self.running:
|
127
|
+
await asyncio.sleep(2)
|
128
|
+
|
129
|
+
# Startup fabric. This runs in 'async' mode, creates a taskgroup and
|
130
|
+
# runs the producer.
|
131
|
+
@classmethod
|
132
|
+
async def launch_async(cls, args):
|
133
|
+
|
134
|
+
try:
|
135
|
+
|
136
|
+
# Create a taskgroup. This seems complicated, when an exception
|
137
|
+
# occurs, unhandled it looks like it cancels all threads in the
|
138
|
+
# taskgroup. Needs the exception to be caught in the right
|
139
|
+
# place.
|
140
|
+
async with asyncio.TaskGroup() as tg:
|
141
|
+
|
142
|
+
|
143
|
+
# Create a processor instance, and include the taskgroup
|
144
|
+
# as a paramter. A processor identity ident is used as
|
145
|
+
# - subscriber name
|
146
|
+
# - an identifier for flow configuration
|
147
|
+
p = cls(**args | { "taskgroup": tg })
|
148
|
+
|
149
|
+
# Start the processor
|
150
|
+
await p.start()
|
151
|
+
|
152
|
+
# Run the processor
|
153
|
+
task = tg.create_task(p.run())
|
154
|
+
|
155
|
+
# The taskgroup causes everything to wait until
|
156
|
+
# all threads have stopped
|
157
|
+
|
158
|
+
# This is here to output a debug message, shouldn't be needed.
|
159
|
+
except Exception as e:
|
160
|
+
print("Exception, closing taskgroup", flush=True)
|
161
|
+
raise e
|
162
|
+
|
163
|
+
# Startup fabric. launch calls launch_async in async mode.
|
164
|
+
@classmethod
|
165
|
+
def launch(cls, ident, doc):
|
166
|
+
|
167
|
+
# Start assembling CLI arguments
|
168
|
+
parser = argparse.ArgumentParser(
|
169
|
+
prog=ident,
|
170
|
+
description=doc
|
171
|
+
)
|
172
|
+
|
173
|
+
parser.add_argument(
|
174
|
+
'--id',
|
175
|
+
default=ident,
|
176
|
+
help=f'Configuration identity (default: {ident})',
|
177
|
+
)
|
178
|
+
|
179
|
+
# Invoke the class-specific add_args, which manages adding all the
|
180
|
+
# command-line arguments
|
181
|
+
cls.add_args(parser)
|
182
|
+
|
183
|
+
# Parse arguments
|
184
|
+
args = parser.parse_args()
|
185
|
+
args = vars(args)
|
186
|
+
|
187
|
+
# Debug
|
188
|
+
print(args, flush=True)
|
189
|
+
|
190
|
+
# Start the Prometheus metrics service if needed
|
191
|
+
if args["metrics"]:
|
192
|
+
start_http_server(args["metrics_port"])
|
193
|
+
|
194
|
+
# Loop forever, exception handler
|
195
|
+
while True:
|
196
|
+
|
197
|
+
print("Starting...", flush=True)
|
198
|
+
|
199
|
+
try:
|
200
|
+
|
201
|
+
# Launch the processor in an asyncio handler
|
202
|
+
asyncio.run(cls.launch_async(
|
203
|
+
args
|
204
|
+
))
|
205
|
+
|
206
|
+
except KeyboardInterrupt:
|
207
|
+
print("Keyboard interrupt.", flush=True)
|
208
|
+
return
|
209
|
+
|
210
|
+
except _pulsar.Interrupted:
|
211
|
+
print("Pulsar Interrupted.", flush=True)
|
212
|
+
return
|
213
|
+
|
214
|
+
# Exceptions from a taskgroup come in as an exception group
|
215
|
+
except ExceptionGroup as e:
|
216
|
+
|
217
|
+
print("Exception group:", flush=True)
|
218
|
+
|
219
|
+
for se in e.exceptions:
|
220
|
+
print(" Type:", type(se), flush=True)
|
221
|
+
print(f" Exception: {se}", flush=True)
|
222
|
+
|
223
|
+
except Exception as e:
|
224
|
+
print("Type:", type(e), flush=True)
|
225
|
+
print("Exception:", e, flush=True)
|
226
|
+
|
227
|
+
# Retry occurs here
|
228
|
+
print("Will retry...", flush=True)
|
229
|
+
time.sleep(4)
|
230
|
+
print("Retrying...", flush=True)
|
231
|
+
|
232
|
+
# The command-line arguments are built using a stack of add_args
|
233
|
+
# invocations
|
234
|
+
@staticmethod
|
235
|
+
def add_args(parser):
|
236
|
+
|
237
|
+
PulsarClient.add_args(parser)
|
238
|
+
|
239
|
+
parser.add_argument(
|
240
|
+
'--config-queue',
|
241
|
+
default=default_config_queue,
|
242
|
+
help=f'Config push queue {default_config_queue}',
|
243
|
+
)
|
244
|
+
|
245
|
+
parser.add_argument(
|
246
|
+
'--metrics',
|
247
|
+
action=argparse.BooleanOptionalAction,
|
248
|
+
default=True,
|
249
|
+
help=f'Metrics enabled (default: true)',
|
250
|
+
)
|
251
|
+
|
252
|
+
parser.add_argument(
|
253
|
+
'-P', '--metrics-port',
|
254
|
+
type=int,
|
255
|
+
default=8000,
|
256
|
+
help=f'Pulsar host (default: 8000)',
|
257
|
+
)
|