guardianhub 0.1.44__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 (53) hide show
  1. guardianhub-0.1.44/LICENSE +21 -0
  2. guardianhub-0.1.44/PKG-INFO +120 -0
  3. guardianhub-0.1.44/README.md +84 -0
  4. guardianhub-0.1.44/pyproject.toml +49 -0
  5. guardianhub-0.1.44/src/guardianhub/__init__.py +17 -0
  6. guardianhub-0.1.44/src/guardianhub/_version.py +3 -0
  7. guardianhub-0.1.44/src/guardianhub/agents/runtime.py +12 -0
  8. guardianhub-0.1.44/src/guardianhub/auth/token_provider.py +22 -0
  9. guardianhub-0.1.44/src/guardianhub/clients/__init__.py +2 -0
  10. guardianhub-0.1.44/src/guardianhub/clients/classification_client.py +52 -0
  11. guardianhub-0.1.44/src/guardianhub/clients/graph_db_client.py +161 -0
  12. guardianhub-0.1.44/src/guardianhub/clients/llm.py +2 -0
  13. guardianhub-0.1.44/src/guardianhub/clients/llm_client.py +145 -0
  14. guardianhub-0.1.44/src/guardianhub/clients/llm_service.py +297 -0
  15. guardianhub-0.1.44/src/guardianhub/clients/metadata_extractor_client.py +53 -0
  16. guardianhub-0.1.44/src/guardianhub/clients/ocr_client.py +80 -0
  17. guardianhub-0.1.44/src/guardianhub/clients/paperless_client.py +518 -0
  18. guardianhub-0.1.44/src/guardianhub/clients/registry_client.py +18 -0
  19. guardianhub-0.1.44/src/guardianhub/clients/text_cleaner_client.py +53 -0
  20. guardianhub-0.1.44/src/guardianhub/clients/vector_client.py +345 -0
  21. guardianhub-0.1.44/src/guardianhub/config/__init__.py +2 -0
  22. guardianhub-0.1.44/src/guardianhub/config/config.json +39 -0
  23. guardianhub-0.1.44/src/guardianhub/config/config_dev.json +41 -0
  24. guardianhub-0.1.44/src/guardianhub/config/settings.py +137 -0
  25. guardianhub-0.1.44/src/guardianhub/http/http_client.py +26 -0
  26. guardianhub-0.1.44/src/guardianhub/logging/__init__.py +2 -0
  27. guardianhub-0.1.44/src/guardianhub/logging/logging.py +70 -0
  28. guardianhub-0.1.44/src/guardianhub/models/__init__.py +1 -0
  29. guardianhub-0.1.44/src/guardianhub/models/base.py +2 -0
  30. guardianhub-0.1.44/src/guardianhub/models/registry/client.py +16 -0
  31. guardianhub-0.1.44/src/guardianhub/models/registry/dynamic_loader.py +91 -0
  32. guardianhub-0.1.44/src/guardianhub/models/registry/loader.py +37 -0
  33. guardianhub-0.1.44/src/guardianhub/models/registry/registry.py +17 -0
  34. guardianhub-0.1.44/src/guardianhub/models/registry/signing.py +70 -0
  35. guardianhub-0.1.44/src/guardianhub/models/template/__init__.py +0 -0
  36. guardianhub-0.1.44/src/guardianhub/models/template/agent_response_evaluation.py +62 -0
  37. guardianhub-0.1.44/src/guardianhub/models/template/extraction.py +29 -0
  38. guardianhub-0.1.44/src/guardianhub/models/template/suggestion.py +42 -0
  39. guardianhub-0.1.44/src/guardianhub/observability/__init__.py +2 -0
  40. guardianhub-0.1.44/src/guardianhub/observability/instrumentation.py +267 -0
  41. guardianhub-0.1.44/src/guardianhub/prompts/base.py +7 -0
  42. guardianhub-0.1.44/src/guardianhub/prompts/providers/langfuse_provider.py +13 -0
  43. guardianhub-0.1.44/src/guardianhub/prompts/providers/local_provider.py +22 -0
  44. guardianhub-0.1.44/src/guardianhub/prompts/registry.py +14 -0
  45. guardianhub-0.1.44/src/guardianhub/scripts/script.sh +31 -0
  46. guardianhub-0.1.44/src/guardianhub/services/base.py +16 -0
  47. guardianhub-0.1.44/src/guardianhub/template/__init__.py +0 -0
  48. guardianhub-0.1.44/src/guardianhub/tools/gh_registry_cli.py +172 -0
  49. guardianhub-0.1.44/src/guardianhub/utils/__init__.py +0 -0
  50. guardianhub-0.1.44/src/guardianhub/utils/app_state.py +74 -0
  51. guardianhub-0.1.44/src/guardianhub/utils/fastapi_utils.py +141 -0
  52. guardianhub-0.1.44/src/guardianhub/utils/json_utils.py +139 -0
  53. guardianhub-0.1.44/src/guardianhub/utils/metrics.py +60 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Rashmi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,120 @@
1
+ Metadata-Version: 2.4
2
+ Name: guardianhub
3
+ Version: 0.1.44
4
+ Summary: Unified SDK for Local AI Guardian
5
+ License: MIT
6
+ License-File: LICENSE
7
+ Author: Jayant
8
+ Author-email: jayant@yantramops.com
9
+ Requires-Python: >=3.10
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
17
+ Provides-Extra: dev
18
+ Requires-Dist: click (>=8.0.0)
19
+ Requires-Dist: fastapi (>=0.123.0)
20
+ Requires-Dist: httpcore (>=0.17.0)
21
+ Requires-Dist: httpx (>=0.24.0)
22
+ Requires-Dist: opentelemetry-api (>=1.38.0)
23
+ Requires-Dist: opentelemetry-exporter-otlp-proto-http (>=1.38.0)
24
+ Requires-Dist: opentelemetry-instrumentation-fastapi (>=0.59b0)
25
+ Requires-Dist: opentelemetry-instrumentation-httpx (>=0.59b0)
26
+ Requires-Dist: opentelemetry-sdk (>=1.38.0)
27
+ Requires-Dist: prometheus-client (>=0.23.1)
28
+ Requires-Dist: pycryptodome (>=3.18.0)
29
+ Requires-Dist: pydantic (>=2.0.0)
30
+ Requires-Dist: pydantic-settings (>=2.0.0)
31
+ Requires-Dist: pytest (>=7.0.0) ; extra == "dev"
32
+ Requires-Dist: pytest-asyncio (>=0.20.0) ; extra == "dev"
33
+ Project-URL: Homepage, https://github.com/yantramai/guardianhub-sdk
34
+ Project-URL: Source, https://github.com/yantramai/guardianhub-sdk
35
+ Description-Content-Type: text/markdown
36
+
37
+ # GuardianHub SDK
38
+
39
+ A Python SDK for interacting with GuardianHub services, featuring a model registry and LLM client.
40
+
41
+ ## Features
42
+
43
+ - Model registry with versioning
44
+ - Async HTTP client for API interactions
45
+ - Pydantic model support
46
+ - Local development and testing tools
47
+
48
+ ## Installation
49
+
50
+ ```bash
51
+ # Install from PyPI (when published)
52
+ ssh-keygen -t ed25519 -C "rashmi@yantramops.com"
53
+ pip install guardianhub-sdk
54
+
55
+ # Or install in development mode
56
+ pip install -e .[test]
57
+
58
+ # 1. Remove the specified directory and its contents from the Git index.
59
+ # The --cached flag ensures the files are NOT deleted from your local disk.
60
+ git rm -r --cached .github
61
+
62
+ # 2. Add the .gitignore file (which now contains the new rules)
63
+ git add .gitignore
64
+
65
+ # 3. Commit the changes
66
+ git commit -m "Cleanup: Remove .github directory from tracking and update .gitignore"
67
+
68
+ git checkout -b dev
69
+ git push -u origin dev
70
+
71
+
72
+ git checkout dev
73
+ git pull origin dev # Ensure you have the latest code
74
+ git checkout -b feature/model-registry # Name your branch descriptively
75
+ git push -u origin feature/model-registry
76
+
77
+ ```
78
+
79
+ ## Usage
80
+
81
+ ```python
82
+ from guardianhub_sdk.models.registry.loader import RegistryLoader
83
+
84
+ # Initialize the loader
85
+ loader = RegistryLoader()
86
+
87
+ # Load a model
88
+ model = await loader.load_model("UserModel", version="v1")
89
+ ```
90
+
91
+ ## Development
92
+
93
+ ### Setup
94
+
95
+ 1. Clone the repository:
96
+ ```bash
97
+ git clone https://github.com/your-username/guardianhub-sdk.git
98
+ cd guardianhub-sdk
99
+ ```
100
+
101
+ 2. Create a virtual environment:
102
+ ```bash
103
+ python -m venv .venv
104
+ source .venv/bin/activate # On Windows: .venv\Scripts\activate
105
+ ```
106
+
107
+ 3. Install dependencies:
108
+ ```bash
109
+ pip install -e .[test]
110
+ ```
111
+
112
+ ### Running Tests
113
+
114
+ ```bash
115
+ pytest tests/
116
+ ```
117
+
118
+ ## License
119
+
120
+ MIT
@@ -0,0 +1,84 @@
1
+ # GuardianHub SDK
2
+
3
+ A Python SDK for interacting with GuardianHub services, featuring a model registry and LLM client.
4
+
5
+ ## Features
6
+
7
+ - Model registry with versioning
8
+ - Async HTTP client for API interactions
9
+ - Pydantic model support
10
+ - Local development and testing tools
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ # Install from PyPI (when published)
16
+ ssh-keygen -t ed25519 -C "rashmi@yantramops.com"
17
+ pip install guardianhub-sdk
18
+
19
+ # Or install in development mode
20
+ pip install -e .[test]
21
+
22
+ # 1. Remove the specified directory and its contents from the Git index.
23
+ # The --cached flag ensures the files are NOT deleted from your local disk.
24
+ git rm -r --cached .github
25
+
26
+ # 2. Add the .gitignore file (which now contains the new rules)
27
+ git add .gitignore
28
+
29
+ # 3. Commit the changes
30
+ git commit -m "Cleanup: Remove .github directory from tracking and update .gitignore"
31
+
32
+ git checkout -b dev
33
+ git push -u origin dev
34
+
35
+
36
+ git checkout dev
37
+ git pull origin dev # Ensure you have the latest code
38
+ git checkout -b feature/model-registry # Name your branch descriptively
39
+ git push -u origin feature/model-registry
40
+
41
+ ```
42
+
43
+ ## Usage
44
+
45
+ ```python
46
+ from guardianhub_sdk.models.registry.loader import RegistryLoader
47
+
48
+ # Initialize the loader
49
+ loader = RegistryLoader()
50
+
51
+ # Load a model
52
+ model = await loader.load_model("UserModel", version="v1")
53
+ ```
54
+
55
+ ## Development
56
+
57
+ ### Setup
58
+
59
+ 1. Clone the repository:
60
+ ```bash
61
+ git clone https://github.com/your-username/guardianhub-sdk.git
62
+ cd guardianhub-sdk
63
+ ```
64
+
65
+ 2. Create a virtual environment:
66
+ ```bash
67
+ python -m venv .venv
68
+ source .venv/bin/activate # On Windows: .venv\Scripts\activate
69
+ ```
70
+
71
+ 3. Install dependencies:
72
+ ```bash
73
+ pip install -e .[test]
74
+ ```
75
+
76
+ ### Running Tests
77
+
78
+ ```bash
79
+ pytest tests/
80
+ ```
81
+
82
+ ## License
83
+
84
+ MIT
@@ -0,0 +1,49 @@
1
+ [build-system]
2
+ requires = ["poetry-core>=1.0.0"]
3
+ build-backend = "poetry.core.masonry.api"
4
+
5
+ [project]
6
+ name = "guardianhub"
7
+ version = "0.1.44" # This will be overridden by Hatch's dynamic version
8
+ # Note: Keep _version.py as the source of truth for version
9
+ description = "Unified SDK for Local AI Guardian"
10
+ authors = [
11
+ {name = "Jayant", email = "jayant@yantramops.com"}
12
+ ]
13
+ readme = "README.md"
14
+ license = {text = "MIT"}
15
+ requires-python = ">=3.10"
16
+ dependencies = [
17
+ "httpx>=0.24.0",
18
+ "pydantic>=2.0.0",
19
+ "pydantic-settings>=2.0.0",
20
+ "pycryptodome>=3.18.0",
21
+ "click>=8.0.0",
22
+ "httpcore>=0.17.0",
23
+ "prometheus-client>=0.23.1",
24
+ "fastapi>=0.123.0",
25
+ "opentelemetry-api>=1.38.0",
26
+ "opentelemetry-sdk>=1.38.0",
27
+ "opentelemetry-exporter-otlp-proto-http>=1.38.0",
28
+ "opentelemetry-instrumentation-fastapi>=0.59b0",
29
+ "opentelemetry-instrumentation-httpx>=0.59b0",
30
+ ]
31
+
32
+ [project.optional-dependencies]
33
+ dev = [
34
+ "pytest>=7.0.0",
35
+ "pytest-asyncio>=0.20.0"
36
+ ]
37
+
38
+ [project.urls]
39
+ Homepage = "https://github.com/yantramai/guardianhub-sdk"
40
+ Source = "https://github.com/yantramai/guardianhub-sdk"
41
+
42
+ [tool.hatch.version]
43
+ path = "src/guardianhub/_version.py"
44
+
45
+ [tool.hatch.build.targets.wheel]
46
+ packages = ["src/guardianhub"]
47
+
48
+ [tool.hatch.build.targets.sdist]
49
+ include = ["src/guardianhub"]
@@ -0,0 +1,17 @@
1
+ # src/guardianhub/__init__.py
2
+ from ._version import __version__ # version exported for users
3
+
4
+ from .logging import get_logger,setup_logging
5
+ from .utils.app_state import AppState
6
+ from .utils.metrics import setup_metrics, get_metrics_registry
7
+ from .utils.fastapi_utils import setup_middleware, setup_health_endpoint, setup_metrics_endpoint
8
+ from .observability.instrumentation import configure_instrumentation
9
+
10
+ # Expose public modules (minimal)
11
+ __all__ = ["__version__",
12
+ "get_logger","setup_logging",
13
+ "AppState",
14
+ "setup_metrics","get_metrics_registry",
15
+ "setup_middleware","setup_health_endpoint","setup_metrics_endpoint",
16
+ "configure_instrumentation",
17
+ ]
@@ -0,0 +1,3 @@
1
+ # src/guardianhub/_version.py
2
+ # single-source of truth for hatch version plugin
3
+ __version__ = "0.1.44"
@@ -0,0 +1,12 @@
1
+ # guardianhub_sdk/agents/runtime.py
2
+ from typing import Any, Dict
3
+
4
+
5
+ class AgentRuntime:
6
+ def __init__(self, clients: Dict[str, Any]):
7
+ self.clients = clients
8
+
9
+
10
+ async def run(self, spec: Dict[str, Any]):
11
+ # stub: execute agent spec using clients
12
+ return {"status": "ok", "spec": spec}
@@ -0,0 +1,22 @@
1
+ # guardianhub_sdk/auth/token_provider.py
2
+ from typing import Optional
3
+
4
+
5
+ class TokenProvider:
6
+
7
+
8
+ """Simple token provider stub. Replace with your vault/service account integration."""
9
+
10
+
11
+ def __init__(self, api_key: Optional[str] = None):
12
+ self.api_key = api_key
13
+ self._cached_token = api_key
14
+ self._expires_at = None
15
+
16
+
17
+ async def get_token(self) -> str:
18
+ # For server-to-server use, you may want to implement JWT/service-account exchange
19
+ if self._cached_token:
20
+ return self._cached_token
21
+ # fallback placeholder
22
+ return "placeholder"
@@ -0,0 +1,2 @@
1
+ from .vector_client import VectorClient
2
+ from .llm_client import LLMClient
@@ -0,0 +1,52 @@
1
+ """Minio client with graceful error handling and connection management."""
2
+ import logging
3
+ from datetime import timedelta
4
+ from typing import Optional, Union, Dict, Any
5
+
6
+ import httpx
7
+
8
+ from config import settings
9
+ logger = logging.getLogger(__name__)
10
+
11
+ class ClassificationClient:
12
+ """Minio client wrapper with graceful error handling."""
13
+
14
+ def __init__(self, base_url:str, poll_interval: int = 5, poll_timeout: int = 300):
15
+ self.initialized = False
16
+ self.client = None
17
+
18
+ try:
19
+ self.api_url = base_url.rstrip('/')
20
+ self.headers = {
21
+ "Accept": "application/json",
22
+ }
23
+ self.poll_interval = poll_interval
24
+ self.poll_timeout = poll_timeout
25
+
26
+ # Initialize the persistent httpx client here.
27
+ # DO NOT use it in an 'async with' block in methods, or it will be closed.
28
+ self.client = httpx.AsyncClient(headers=self.headers, base_url=self.api_url,
29
+ timeout=self.poll_timeout + 60)
30
+ self.initialized = True
31
+ logger.info(f"✅ Classification client connected to {settings.endpoints.CLASSIFICATION_ENDPOINT}")
32
+
33
+ except Exception as e:
34
+ logger.warning(
35
+ f"⚠️ Failed to initialize Classification client: {str(e)}. "
36
+ "Classification operations will be disabled."
37
+ )
38
+ self.initialized = False
39
+
40
+ async def classify(self, text: str,metadata:Dict[str, Any]) -> Dict[str, Any]:
41
+ """Generate a presigned URL for uploading an object."""
42
+ if not self.initialized:
43
+ logger.warning("Classification client not initialized, cannot generate presigned URL")
44
+ return {}
45
+
46
+ try:
47
+ response = await self.client.post("/v1/document/classify", json={"text": text, "metadata":metadata})
48
+ return response.json()
49
+
50
+ except Exception as e:
51
+ logger.error(f"Failed to classify document: {str(e)}")
52
+ return {}
@@ -0,0 +1,161 @@
1
+ # services/clients/neo4j_client.py (New File/Class)
2
+ import datetime
3
+ import json
4
+ from typing import Dict, Any, Optional
5
+
6
+ import httpx
7
+ import yaml # Must be imported for YAML output
8
+ # Import all model classes to make them available at the package level
9
+ from guardianhub.models.template.suggestion import TemplateSchemaSuggestion
10
+
11
+ from config import settings
12
+ from guardianhub_core import get_logger
13
+ logger = get_logger(__name__)
14
+
15
+ # NOTE: The actual driver must be injected/managed by the calling service (doc-template service)
16
+ class GraphDBClient:
17
+ def __init__(self, base_url: str, poll_interval: int = 5, poll_timeout: int = 300):
18
+ """
19
+ Initializes the Paperless client.
20
+ """
21
+ self.api_url = base_url.rstrip('/')
22
+ self.api_token = settings.endpoints.GRAPH_DB_URL
23
+ self.headers = {
24
+ "Accept": "application/json",
25
+ }
26
+ self.poll_interval = poll_interval
27
+ self.poll_timeout = poll_timeout
28
+
29
+ # Initialize the persistent httpx client here.
30
+ # DO NOT use it in an 'async with' block in methods, or it will be closed.
31
+ self.client = httpx.AsyncClient(headers=self.headers, base_url=self.api_url, timeout=self.poll_timeout + 60)
32
+ logger.info("PaperlessClient initialized for URL: %s", self.api_url)
33
+
34
+ async def save_document_template(self, template: TemplateSchemaSuggestion) -> bool:
35
+ """
36
+ Creates a new DocumentTemplate node and links it to the Doc-Template Service
37
+ node by submitting a YAML payload to the ingestion endpoint.
38
+ """
39
+
40
+ # 1. Prepare Node Properties
41
+ template_properties = {
42
+ "template_id": template.template_id,
43
+ "document_type": template.document_type,
44
+ "template_name": template.template_name,
45
+ "required_keywords": template.required_keywords,
46
+ # The ingestion endpoint requires the schema to be stored as a property value.
47
+ # We must serialize the JSON schema dictionary to a string/YAML-safe string.
48
+ "json_schema_str": json.dumps(template.json_schema)
49
+ }
50
+
51
+ # 2. Construct the Graph Ingestion Dictionary (The YAML Payload Structure)
52
+ ingestion_payload = {
53
+ "nodes": [
54
+ {
55
+ "type": "DocumentTemplate",
56
+ "properties": template_properties
57
+ }
58
+ ],
59
+ "relationships": [
60
+ {
61
+ "from": {
62
+ "type": "PlatformService",
63
+ "property": "name",
64
+ "value": "doc-template-service" # Matching the service node created at startup
65
+ },
66
+ "to": {
67
+ "type": "DocumentTemplate",
68
+ "property": "template_id",
69
+ "value": template.template_id
70
+ },
71
+ "type": "MANAGES_TEMPLATE",
72
+ "properties": {
73
+ "link_date": datetime.datetime.now()
74
+ }
75
+ }
76
+ ]
77
+ }
78
+
79
+ # 3. Convert to YAML
80
+ yaml_payload = yaml.dump(ingestion_payload, sort_keys=False)
81
+
82
+ # 4. POST to the ingestion endpoint
83
+ try:
84
+ response = await self.client.post(
85
+ "/ingest-yaml-schema",
86
+ content=yaml_payload,
87
+ headers={'Content-Type': 'application/x-yaml'},
88
+ timeout=30.0
89
+ )
90
+ response.raise_for_status()
91
+
92
+ response_json = response.json()
93
+ if response_json.get("status") == "success":
94
+ logger.info(f"✅ Template {template.template_id} successfully persisted via YAML ingestion.")
95
+ return True
96
+ else:
97
+ logger.error(
98
+ f"Graph DB Service returned non-success status for template {template.template_id}: {response_json.get('message')}")
99
+ return False
100
+
101
+ except httpx.HTTPStatusError as e:
102
+ logger.error(
103
+ f"HTTP error during YAML ingestion (Template {template.template_id}). Status: {e.response.status_code}. Detail: {e.response.text}",
104
+ exc_info=True
105
+ )
106
+ return False
107
+ except Exception as e:
108
+ logger.error(f"Failed to ingest template YAML for {template.template_id}: {e}", exc_info=True)
109
+ return False
110
+
111
+ # services/clients/neo4j_client.py (Corrected get_template_by_id)
112
+
113
+ async def get_template_by_id(self, template_id: str) -> Optional[Dict[str, Any]]:
114
+ """
115
+ Retrieves the properties of a DocumentTemplate node by its ID via the
116
+ /query-cypher endpoint.
117
+ """
118
+
119
+ # 1. Define the Cypher query
120
+ cypher_query = """
121
+ MATCH (t:DocumentTemplate {template_id: $template_id})
122
+ RETURN properties(t) as template
123
+ """
124
+
125
+ # 2. Prepare the JSON payload for the read endpoint
126
+ payload = {
127
+ "query": cypher_query,
128
+ "parameters": {"template_id": template_id}
129
+ }
130
+
131
+ try:
132
+ # 🛠️ FIX: Target the correct read endpoint and send JSON payload
133
+ response = await self.client.post(
134
+ "/query-cypher", # The correct read endpoint
135
+ json=payload, # Send as JSON
136
+ timeout=30.0
137
+ )
138
+ response.raise_for_status()
139
+
140
+ response_json = response.json()
141
+
142
+ if response_json.get("status") == "success" and response_json.get("results"):
143
+ # The result is typically a list of dicts from Cypher execution
144
+ record = response_json["results"][0]
145
+ template_data = record["template"]
146
+
147
+ # Convert the stored JSON Schema string back to a dictionary
148
+ template_data['json_schema'] = json.loads(template_data.pop('json_schema_str'))
149
+
150
+ logger.info(f"✅ Template {template_id} successfully retrieved from GraphDB.")
151
+ return template_data
152
+ else:
153
+ logger.info(f"Template {template_id} not found or query failed: {response_json.get('message')}")
154
+ return None
155
+
156
+ except httpx.HTTPStatusError as e:
157
+ logger.error(f"HTTP error retrieving template {template_id}. Status: {e.response.status_code}")
158
+ return None
159
+ except Exception as e:
160
+ logger.error(f"Failed to retrieve template {template_id} from Neo4j: {e}", exc_info=True)
161
+ return None
@@ -0,0 +1,2 @@
1
+ class LLMClient:
2
+ pass