graphs.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614
  1. from builtins import list as _list
  2. from typing import Any, Optional
  3. from uuid import UUID
  4. from shared.api.models import (
  5. WrappedBooleanResponse,
  6. WrappedCommunitiesResponse,
  7. WrappedCommunityResponse,
  8. WrappedEntitiesResponse,
  9. WrappedEntityResponse,
  10. WrappedGenericMessageResponse,
  11. WrappedGraphResponse,
  12. WrappedGraphsResponse,
  13. WrappedRelationshipResponse,
  14. WrappedRelationshipsResponse,
  15. )
  16. class GraphsSDK:
  17. """SDK for interacting with knowledge graphs in the v3 API."""
  18. def __init__(self, client):
  19. self.client = client
  20. async def list(
  21. self,
  22. collection_ids: Optional[list[str | UUID]] = None,
  23. offset: Optional[int] = 0,
  24. limit: Optional[int] = 100,
  25. ) -> WrappedGraphsResponse:
  26. """List graphs with pagination and filtering options.
  27. Args:
  28. ids (Optional[list[str | UUID]]): Filter graphs by ids
  29. offset (int, optional): Specifies the number of objects to skip. Defaults to 0.
  30. limit (int, optional): Specifies a limit on the number of objects to return, ranging between 1 and 100. Defaults to 100.
  31. Returns:
  32. WrappedGraphsResponse
  33. """
  34. params: dict = {
  35. "offset": offset,
  36. "limit": limit,
  37. }
  38. if collection_ids:
  39. params["collection_ids"] = collection_ids
  40. response_dict = await self.client._make_request(
  41. "GET", "graphs", params=params, version="v3"
  42. )
  43. return WrappedGraphsResponse(**response_dict)
  44. async def retrieve(
  45. self,
  46. collection_id: str | UUID,
  47. ) -> WrappedGraphResponse:
  48. """Get detailed information about a specific graph.
  49. Args:
  50. collection_id (str | UUID): Graph ID to retrieve
  51. Returns:
  52. WrappedGraphResponse
  53. """
  54. response_dict = await self.client._make_request(
  55. "GET", f"graphs/{str(collection_id)}", version="v3"
  56. )
  57. return WrappedGraphResponse(**response_dict)
  58. async def reset(
  59. self,
  60. collection_id: str | UUID,
  61. ) -> WrappedBooleanResponse:
  62. """Deletes a graph and all its associated data.
  63. This endpoint permanently removes the specified graph along with all
  64. entities and relationships that belong to only this graph.
  65. Entities and relationships extracted from documents are not deleted.
  66. Args:
  67. collection_id (str | UUID): Graph ID to reset
  68. Returns:
  69. WrappedBooleanResponse
  70. """
  71. response_dict = await self.client._make_request(
  72. "POST", f"graphs/{str(collection_id)}/reset", version="v3"
  73. )
  74. return WrappedBooleanResponse(**response_dict)
  75. async def update(
  76. self,
  77. collection_id: str | UUID,
  78. name: Optional[str] = None,
  79. description: Optional[str] = None,
  80. ) -> WrappedGraphResponse:
  81. """Update graph information.
  82. Args:
  83. collection_id (str | UUID): The collection ID corresponding to the graph
  84. name (Optional[str]): Optional new name for the graph
  85. description (Optional[str]): Optional new description for the graph
  86. Returns:
  87. WrappedGraphResponse
  88. """
  89. data: dict[str, Any] = {}
  90. if name is not None:
  91. data["name"] = name
  92. if description is not None:
  93. data["description"] = description
  94. response_dict = await self.client._make_request(
  95. "POST",
  96. f"graphs/{str(collection_id)}",
  97. json=data,
  98. version="v3",
  99. )
  100. return WrappedGraphResponse(**response_dict)
  101. async def list_entities(
  102. self,
  103. collection_id: str | UUID,
  104. offset: Optional[int] = 0,
  105. limit: Optional[int] = 100,
  106. ) -> WrappedEntitiesResponse:
  107. """List entities in a graph.
  108. Args:
  109. collection_id (str | UUID): Graph ID to list entities from
  110. offset (int, optional): Specifies the number of objects to skip. Defaults to 0.
  111. limit (int, optional): Specifies a limit on the number of objects to return, ranging between 1 and 100. Defaults to 100.
  112. Returns:
  113. WrappedEntitiesResponse
  114. """
  115. params: dict = {
  116. "offset": offset,
  117. "limit": limit,
  118. }
  119. response_dict = await self.client._make_request(
  120. "GET",
  121. f"graphs/{str(collection_id)}/entities",
  122. params=params,
  123. version="v3",
  124. )
  125. return WrappedEntitiesResponse(**response_dict)
  126. async def get_entity(
  127. self,
  128. collection_id: str | UUID,
  129. entity_id: str | UUID,
  130. ) -> WrappedEntityResponse:
  131. """Get entity information in a graph.
  132. Args:
  133. collection_id (str | UUID): The collection ID corresponding to the graph
  134. entity_id (str | UUID): Entity ID to get from the graph
  135. Returns:
  136. WrappedEntityResponse
  137. """
  138. response_dict = await self.client._make_request(
  139. "GET",
  140. f"graphs/{str(collection_id)}/entities/{str(entity_id)}",
  141. version="v3",
  142. )
  143. return WrappedEntityResponse(**response_dict)
  144. async def remove_entity(
  145. self,
  146. collection_id: str | UUID,
  147. entity_id: str | UUID,
  148. ) -> WrappedBooleanResponse:
  149. """Remove an entity from a graph.
  150. Args:
  151. collection_id (str | UUID): The collection ID corresponding to the graph
  152. entity_id (str | UUID): Entity ID to remove from the graph
  153. Returns:
  154. WrappedBooleanResponse
  155. """
  156. return await self.client._make_request(
  157. "DELETE",
  158. f"graphs/{str(collection_id)}/entities/{str(entity_id)}",
  159. version="v3",
  160. )
  161. async def list_relationships(
  162. self,
  163. collection_id: str | UUID,
  164. offset: Optional[int] = 0,
  165. limit: Optional[int] = 100,
  166. ) -> WrappedRelationshipsResponse:
  167. """List relationships in a graph.
  168. Args:
  169. collection_id (str | UUID): The collection ID corresponding to the graph
  170. offset (int, optional): Specifies the number of objects to skip. Defaults to 0.
  171. limit (int, optional): Specifies a limit on the number of objects to return, ranging between 1 and 100. Defaults to 100.
  172. Returns:
  173. WrappedRelationshipsResponse
  174. """
  175. params: dict = {
  176. "offset": offset,
  177. "limit": limit,
  178. }
  179. response_dict = await self.client._make_request(
  180. "GET",
  181. f"graphs/{str(collection_id)}/relationships",
  182. params=params,
  183. version="v3",
  184. )
  185. return WrappedRelationshipsResponse(**response_dict)
  186. async def get_relationship(
  187. self,
  188. collection_id: str | UUID,
  189. relationship_id: str | UUID,
  190. ) -> WrappedRelationshipResponse:
  191. """Get relationship information in a graph.
  192. Args:
  193. collection_id (str | UUID): The collection ID corresponding to the graph
  194. relationship_id (str | UUID): Relationship ID to get from the graph
  195. Returns:
  196. WrappedRelationshipResponse
  197. """
  198. response_dict = await self.client._make_request(
  199. "GET",
  200. f"graphs/{str(collection_id)}/relationships/{str(relationship_id)}",
  201. version="v3",
  202. )
  203. return WrappedRelationshipResponse(**response_dict)
  204. async def remove_relationship(
  205. self,
  206. collection_id: str | UUID,
  207. relationship_id: str | UUID,
  208. ) -> WrappedBooleanResponse:
  209. """Remove a relationship from a graph.
  210. Args:
  211. collection_id (str | UUID): The collection ID corresponding to the graph
  212. relationship_id (str | UUID): Relationship ID to remove from the graph
  213. Returns:
  214. WrappedBooleanResponse
  215. """
  216. response_dict = await self.client._make_request(
  217. "DELETE",
  218. f"graphs/{str(collection_id)}/relationships/{str(relationship_id)}",
  219. version="v3",
  220. )
  221. return WrappedBooleanResponse(**response_dict)
  222. async def build(
  223. self,
  224. collection_id: str | UUID,
  225. settings: Optional[dict] = None,
  226. run_with_orchestration: bool = True,
  227. ) -> WrappedGenericMessageResponse:
  228. """Build a graph.
  229. Args:
  230. collection_id (str | UUID): The collection ID corresponding to the graph
  231. settings (dict): Settings for the build
  232. run_with_orchestration (bool, optional): Whether to run with orchestration. Defaults to True.
  233. Returns:
  234. WrappedGenericMessageResponse
  235. """
  236. data: dict[str, Any] = {
  237. "run_with_orchestration": run_with_orchestration,
  238. }
  239. if settings:
  240. data["settings"] = settings
  241. response_dict = await self.client._make_request(
  242. "POST",
  243. f"graphs/{str(collection_id)}/communities/build",
  244. json=data,
  245. version="v3",
  246. )
  247. return WrappedGenericMessageResponse(**response_dict)
  248. async def list_communities(
  249. self,
  250. collection_id: str | UUID,
  251. offset: Optional[int] = 0,
  252. limit: Optional[int] = 100,
  253. ) -> WrappedCommunitiesResponse:
  254. """List communities in a graph.
  255. Args:
  256. collection_id (str | UUID): The collection ID corresponding to the graph
  257. offset (int, optional): Specifies the number of objects to skip. Defaults to 0.
  258. limit (int, optional): Specifies a limit on the number of objects to return, ranging between 1 and 100. Defaults to 100.
  259. Returns:
  260. WrappedCommunitiesResponse
  261. """
  262. params: dict = {
  263. "offset": offset,
  264. "limit": limit,
  265. }
  266. response_dict = await self.client._make_request(
  267. "GET",
  268. f"graphs/{str(collection_id)}/communities",
  269. params=params,
  270. version="v3",
  271. )
  272. return WrappedCommunitiesResponse(**response_dict)
  273. async def get_community(
  274. self,
  275. collection_id: str | UUID,
  276. community_id: str | UUID,
  277. ) -> WrappedCommunityResponse:
  278. """Get community information in a graph.
  279. Args:
  280. collection_id (str | UUID): The collection ID corresponding to the graph
  281. community_id (str | UUID): Community ID to get from the graph
  282. Returns:
  283. WrappedCommunityResponse
  284. """
  285. response_dict = await self.client._make_request(
  286. "GET",
  287. f"graphs/{str(collection_id)}/communities/{str(community_id)}",
  288. version="v3",
  289. )
  290. return WrappedCommunityResponse(**response_dict)
  291. async def update_community(
  292. self,
  293. collection_id: str | UUID,
  294. community_id: str | UUID,
  295. name: Optional[str] = None,
  296. summary: Optional[str] = None,
  297. findings: Optional[_list[str]] = None,
  298. rating: Optional[int] = None,
  299. rating_explanation: Optional[str] = None,
  300. level: Optional[int] = None,
  301. attributes: Optional[dict] = None,
  302. ) -> WrappedCommunityResponse:
  303. """Update community information.
  304. Args:
  305. collection_id (str | UUID): The collection ID corresponding to the graph
  306. community_id (str | UUID): Community ID to update
  307. name (Optional[str]): Optional new name for the community
  308. summary (Optional[str]): Optional new summary for the community
  309. findings (Optional[list[str]]): Optional new findings for the community
  310. rating (Optional[int]): Optional new rating for the community
  311. rating_explanation (Optional[str]): Optional new rating explanation for the community
  312. level (Optional[int]): Optional new level for the community
  313. attributes (Optional[dict]): Optional new attributes for the community
  314. Returns:
  315. WrappedCommunityResponse
  316. """
  317. data: dict[str, Any] = {}
  318. if name is not None:
  319. data["name"] = name
  320. if summary is not None:
  321. data["summary"] = summary
  322. if findings is not None:
  323. data["findings"] = findings
  324. if rating is not None:
  325. data["rating"] = str(rating)
  326. if rating_explanation is not None:
  327. data["rating_explanation"] = rating_explanation
  328. if level is not None:
  329. data["level"] = level
  330. if attributes is not None:
  331. data["attributes"] = attributes
  332. response_dict = await self.client._make_request(
  333. "POST",
  334. f"graphs/{str(collection_id)}/communities/{str(community_id)}",
  335. json=data,
  336. version="v3",
  337. )
  338. return WrappedCommunityResponse(**response_dict)
  339. async def delete_community(
  340. self,
  341. collection_id: str | UUID,
  342. community_id: str | UUID,
  343. ) -> WrappedBooleanResponse:
  344. """Remove a community from a graph.
  345. Args:
  346. collection_id (str | UUID): The collection ID corresponding to the graph
  347. community_id (str | UUID): Community ID to remove from the graph
  348. Returns:
  349. WrappedBooleanResponse
  350. """
  351. response_dict = await self.client._make_request(
  352. "DELETE",
  353. f"graphs/{str(collection_id)}/communities/{str(community_id)}",
  354. version="v3",
  355. )
  356. return WrappedBooleanResponse(**response_dict)
  357. async def pull(
  358. self,
  359. collection_id: str | UUID,
  360. ) -> WrappedBooleanResponse:
  361. """Adds documents to a graph by copying their entities and
  362. relationships.
  363. This endpoint:
  364. 1. Copies document entities to the graphs_entities table
  365. 2. Copies document relationships to the graphs_relationships table
  366. 3. Associates the documents with the graph
  367. When a document is added:
  368. - Its entities and relationships are copied to graph-specific tables
  369. - Existing entities/relationships are updated by merging their properties
  370. - The document ID is recorded in the graph's document_ids array
  371. Documents added to a graph will contribute their knowledge to:
  372. - Graph analysis and querying
  373. - Community detection
  374. - Knowledge graph enrichment
  375. Returns:
  376. WrappedBooleanResponse
  377. """
  378. response_dict = await self.client._make_request(
  379. "POST",
  380. f"graphs/{str(collection_id)}/pull",
  381. version="v3",
  382. )
  383. return WrappedBooleanResponse(**response_dict)
  384. async def remove_document(
  385. self,
  386. collection_id: str | UUID,
  387. document_id: str | UUID,
  388. ) -> WrappedBooleanResponse:
  389. """Removes a document from a graph and removes any associated entities.
  390. This endpoint:
  391. 1. Removes the document ID from the graph's document_ids array
  392. 2. Optionally deletes the document's copied entities and relationships
  393. The user must have access to both the graph and the document being removed.
  394. Returns:
  395. WrappedBooleanResponse
  396. """
  397. response_dict = await self.client._make_request(
  398. "DELETE",
  399. f"graphs/{str(collection_id)}/documents/{str(document_id)}",
  400. version="v3",
  401. )
  402. return WrappedBooleanResponse(**response_dict)
  403. async def create_entity(
  404. self,
  405. collection_id: str | UUID,
  406. name: str,
  407. description: str,
  408. category: Optional[str] = None,
  409. metadata: Optional[dict] = None,
  410. ) -> WrappedEntityResponse:
  411. """Creates a new entity in the graph.
  412. Args:
  413. collection_id (str | UUID): The collection ID corresponding to the graph
  414. name (str): The name of the entity to create
  415. description (Optional[str]): The description of the entity
  416. category (Optional[str]): The category of the entity
  417. metadata (Optional[dict]): Additional metadata for the entity
  418. Returns:
  419. WrappedEntityResponse
  420. """
  421. data: dict[str, Any] = {
  422. "name": name,
  423. "description": description,
  424. }
  425. if category is not None:
  426. data["category"] = category
  427. if metadata is not None:
  428. data["metadata"] = metadata
  429. response_dict = await self.client._make_request(
  430. "POST",
  431. f"graphs/{str(collection_id)}/entities",
  432. json=data,
  433. version="v3",
  434. )
  435. return WrappedEntityResponse(**response_dict)
  436. async def create_relationship(
  437. self,
  438. collection_id: str | UUID,
  439. subject: str,
  440. subject_id: str | UUID,
  441. predicate: str,
  442. object: str,
  443. object_id: str | UUID,
  444. description: str,
  445. weight: Optional[float] = None,
  446. metadata: Optional[dict] = None,
  447. ) -> WrappedRelationshipResponse:
  448. """Creates a new relationship in the graph.
  449. Args:
  450. collection_id (str | UUID): The collection ID corresponding to the graph
  451. subject (str): The subject of the relationship
  452. subject_id (str | UUID): The ID of the subject entity
  453. predicate (str): The predicate/type of the relationship
  454. object (str): The object of the relationship
  455. object_id (str | UUID): The ID of the object entity
  456. description (Optional[str]): Description of the relationship
  457. weight (Optional[float]): Weight/strength of the relationship
  458. metadata (Optional[dict]): Additional metadata for the relationship
  459. Returns:
  460. WrappedRelationshipResponse
  461. """
  462. data: dict[str, Any] = {
  463. "subject": subject,
  464. "subject_id": str(subject_id),
  465. "predicate": predicate,
  466. "object": object,
  467. "object_id": str(object_id),
  468. "description": description,
  469. }
  470. if weight is not None:
  471. data["weight"] = weight
  472. if metadata is not None:
  473. data["metadata"] = metadata
  474. response_dict = await self.client._make_request(
  475. "POST",
  476. f"graphs/{str(collection_id)}/relationships",
  477. json=data,
  478. version="v3",
  479. )
  480. return WrappedRelationshipResponse(**response_dict)
  481. async def create_community(
  482. self,
  483. collection_id: str | UUID,
  484. name: str,
  485. summary: str,
  486. findings: Optional[_list[str]] = None,
  487. rating: Optional[float] = None,
  488. rating_explanation: Optional[str] = None,
  489. ) -> WrappedCommunityResponse:
  490. """Creates a new community in the graph.
  491. Args:
  492. collection_id (str | UUID): The collection ID corresponding to the graph
  493. name (str): The name of the community
  494. summary (str): A summary description of the community
  495. findings (Optional[list[str]]): List of findings about the community
  496. rating (Optional[float]): Rating between 1 and 10
  497. rating_explanation (Optional[str]): Explanation for the rating
  498. Returns:
  499. WrappedCommunityResponse
  500. """
  501. data: dict[str, Any] = {
  502. "name": name,
  503. "summary": summary,
  504. }
  505. if findings is not None:
  506. data["findings"] = findings
  507. if rating is not None:
  508. data["rating"] = rating
  509. if rating_explanation is not None:
  510. data["rating_explanation"] = rating_explanation
  511. response_dict = await self.client._make_request(
  512. "POST",
  513. f"graphs/{str(collection_id)}/communities",
  514. json=data,
  515. version="v3",
  516. )
  517. return WrappedCommunityResponse(**response_dict)