helpers.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. from typing import Any, Dict, List, Optional
  2. import requests
  3. from .config import GRAPH, MCP_URL
  4. class GardenLayer:
  5. """Domain helpers that orchestrate MCP calls for your garden project."""
  6. def __init__(self, mcp_url: str = MCP_URL):
  7. self.mcp_url = mcp_url
  8. def _call(self, tool: str, payload: Dict[str, Any]) -> Any:
  9. response = requests.post(self.mcp_url, json={"tool": tool, "input": payload}, timeout=10)
  10. response.raise_for_status()
  11. body = response.json()
  12. if body.get("status") != "ok":
  13. raise RuntimeError(body.get("detail", "MCP tool failed"))
  14. return body["result"]
  15. def traverse(self, subject_uri: str, property_uri: str, direction: str = "outgoing", limit: int = 20) -> Any:
  16. return self._call("traverse_property", {
  17. "subject_uri": subject_uri,
  18. "property_uri": property_uri,
  19. "direction": direction,
  20. "limit": limit,
  21. })
  22. def describe_subject(self, subject_uri: str, limit: int = 10) -> Any:
  23. return self._call("describe_subject", {
  24. "subject_uri": subject_uri,
  25. "limit": limit,
  26. })
  27. def path_traverse(
  28. self,
  29. subject_uri: str,
  30. property_path: List[str],
  31. direction: str = "outgoing",
  32. limit: int = 10,
  33. ) -> Any:
  34. return self._call("path_traverse", {
  35. "subject_uri": subject_uri,
  36. "property_path": property_path,
  37. "direction": direction,
  38. "limit": limit,
  39. })
  40. def property_usage_statistics(self, property_uri: str, examples_limit: int = 5) -> Any:
  41. return self._call("property_usage_statistics", {
  42. "property_uri": property_uri,
  43. "examples_limit": examples_limit,
  44. })
  45. def batch_insert(self, ttl: str, graph: Optional[str] = None) -> Any:
  46. payload = {"ttl": ttl}
  47. if graph:
  48. payload["graph"] = graph
  49. return self._call("batch_insert", payload)
  50. def load_examples(self, files: Optional[List[str]] = None, graph: Optional[str] = None) -> Any:
  51. payload: Dict[str, Any] = {}
  52. if files:
  53. payload["files"] = files
  54. if graph:
  55. payload["graph"] = graph
  56. return self._call("garden_load_examples", payload)
  57. def list_cycle_plants(self, cycle_uri: str, limit: int = 50) -> Any:
  58. return self._call("garden_cycle_plants", {
  59. "cycle_uri": cycle_uri,
  60. "limit": limit,
  61. })
  62. def reassign_cycle(self, subject: str, new_cycle: str, old_cycle: Optional[str] = None, graph: Optional[str] = None) -> Any:
  63. payload: Dict[str, Any] = {
  64. "subject": subject,
  65. "new_cycle": new_cycle,
  66. }
  67. if old_cycle:
  68. payload["old_cycle"] = old_cycle
  69. if graph:
  70. payload["graph"] = graph
  71. return self._call("garden_reassign_cycle", payload)
  72. def add_seedling(
  73. self,
  74. plant_uri: str,
  75. seed_product_uri: str,
  76. cycle_uri: str,
  77. label: str,
  78. strain_uri: str = "http://world.eu.org/example1#Strain_lucky_experimental_line",
  79. graph: str = GRAPH,
  80. ) -> None:
  81. triples = [
  82. ("http://www.w3.org/1999/02/22-rdf-syntax-ns#type", "http://world.eu.org/cannabis-breeding#IndividualPlant", "uri"),
  83. ("http://www.w3.org/2000/01/rdf-schema#label", label, "literal"),
  84. ("http://world.eu.org/cannabis-breeding#inCycle", cycle_uri, "uri"),
  85. ("http://world.eu.org/cannabis-breeding#grownFromSeedProduct", seed_product_uri, "uri"),
  86. ("http://world.eu.org/cannabis-breeding#belongsToStrain", strain_uri, "uri"),
  87. ]
  88. for predicate, obj, obj_type in triples:
  89. self._call("insert_triple", {
  90. "subject": plant_uri,
  91. "predicate": predicate,
  92. "object": obj,
  93. "object_type": obj_type,
  94. "graph": graph,
  95. })