graphs.py 19 KB

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