datarobot-genai 0.2.16__py3-none-any.whl → 0.2.17__py3-none-any.whl

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.
@@ -59,6 +59,22 @@ class ConfluencePage(BaseModel):
59
59
  }
60
60
 
61
61
 
62
+ class ConfluenceComment(BaseModel):
63
+ """Pydantic model for Confluence comment."""
64
+
65
+ comment_id: str = Field(..., description="The unique comment ID")
66
+ page_id: str = Field(..., description="The page ID where the comment was added")
67
+ body: str = Field(..., description="Comment content in storage format")
68
+
69
+ def as_flat_dict(self) -> dict[str, Any]:
70
+ """Return a flat dictionary representation of the comment."""
71
+ return {
72
+ "comment_id": self.comment_id,
73
+ "page_id": self.page_id,
74
+ "body": self.body,
75
+ }
76
+
77
+
62
78
  class ConfluenceClient:
63
79
  """
64
80
  Client for interacting with Confluence API using OAuth access token.
@@ -294,6 +310,78 @@ class ConfluenceClient:
294
310
 
295
311
  return self._parse_response(response.json())
296
312
 
313
+ def _parse_comment_response(self, data: dict, page_id: str) -> ConfluenceComment:
314
+ """Parse API response into ConfluenceComment."""
315
+ body_content = ""
316
+ body = data.get("body", {})
317
+ if isinstance(body, dict):
318
+ storage = body.get("storage", {})
319
+ if isinstance(storage, dict):
320
+ body_content = storage.get("value", "")
321
+
322
+ return ConfluenceComment(
323
+ comment_id=str(data.get("id", "")),
324
+ page_id=page_id,
325
+ body=body_content,
326
+ )
327
+
328
+ async def add_comment(self, page_id: str, comment_body: str) -> ConfluenceComment:
329
+ """
330
+ Add a comment to a Confluence page.
331
+
332
+ Args:
333
+ page_id: The numeric page ID where the comment will be added
334
+ comment_body: The text content of the comment
335
+
336
+ Returns
337
+ -------
338
+ ConfluenceComment with the created comment data
339
+
340
+ Raises
341
+ ------
342
+ ConfluenceError: If page not found, permission denied, or invalid content
343
+ httpx.HTTPStatusError: If the API request fails with unexpected status
344
+ """
345
+ cloud_id = await self._get_cloud_id()
346
+ url = f"{ATLASSIAN_API_BASE}/ex/confluence/{cloud_id}/wiki/rest/api/content"
347
+
348
+ payload: dict[str, Any] = {
349
+ "type": "comment",
350
+ "container": {"id": page_id, "type": "page"},
351
+ "body": {
352
+ "storage": {
353
+ "value": comment_body,
354
+ "representation": "storage",
355
+ }
356
+ },
357
+ }
358
+
359
+ response = await self._client.post(url, json=payload)
360
+
361
+ if response.status_code == HTTPStatus.NOT_FOUND:
362
+ error_msg = self._extract_error_message(response)
363
+ raise ConfluenceError(
364
+ f"Page with ID '{page_id}' not found: {error_msg}",
365
+ status_code=404,
366
+ )
367
+
368
+ if response.status_code == HTTPStatus.FORBIDDEN:
369
+ raise ConfluenceError(
370
+ f"Permission denied: you don't have access to add comments to page '{page_id}'",
371
+ status_code=403,
372
+ )
373
+
374
+ if response.status_code == HTTPStatus.BAD_REQUEST:
375
+ error_msg = self._extract_error_message(response)
376
+ raise ConfluenceError(f"Invalid request: {error_msg}", status_code=400)
377
+
378
+ if response.status_code == HTTPStatus.TOO_MANY_REQUESTS:
379
+ raise ConfluenceError("Rate limit exceeded. Please try again later.", status_code=429)
380
+
381
+ response.raise_for_status()
382
+
383
+ return self._parse_comment_response(response.json(), page_id)
384
+
297
385
  async def __aenter__(self) -> "ConfluenceClient":
298
386
  """Async context manager entry."""
299
387
  return self
@@ -138,3 +138,51 @@ async def confluence_create_page(
138
138
  content=f"New page '{title}' created successfully in space '{space_key}'.",
139
139
  structured_content={"new_page_id": page_response.page_id, "title": page_response.title},
140
140
  )
141
+
142
+
143
+ @dr_mcp_tool(tags={"confluence", "write", "add", "comment"})
144
+ async def confluence_add_comment(
145
+ *,
146
+ page_id: Annotated[str, "The numeric ID of the page where the comment will be added."],
147
+ comment_body: Annotated[str, "The text content of the comment."],
148
+ ) -> ToolResult:
149
+ """Add a new comment to a specified Confluence page for collaboration.
150
+
151
+ Use this tool to add comments to Confluence pages to facilitate collaboration
152
+ and discussion. Comments are added at the page level.
153
+
154
+ Usage:
155
+ - Add comment: page_id="856391684", comment_body="Great work on this documentation!"
156
+ """
157
+ if not page_id:
158
+ raise ToolError("Argument validation error: 'page_id' cannot be empty.")
159
+
160
+ if not comment_body:
161
+ raise ToolError("Argument validation error: 'comment_body' cannot be empty.")
162
+
163
+ access_token = await get_atlassian_access_token()
164
+ if isinstance(access_token, ToolError):
165
+ raise access_token
166
+
167
+ try:
168
+ async with ConfluenceClient(access_token) as client:
169
+ comment_response = await client.add_comment(
170
+ page_id=page_id,
171
+ comment_body=comment_body,
172
+ )
173
+ except ConfluenceError as e:
174
+ logger.error(f"Confluence error adding comment: {e}")
175
+ raise ToolError(str(e))
176
+ except Exception as e:
177
+ logger.error(f"Unexpected error adding comment to Confluence page: {e}")
178
+ raise ToolError(
179
+ f"An unexpected error occurred while adding comment to page '{page_id}': {str(e)}"
180
+ )
181
+
182
+ return ToolResult(
183
+ content=f"Comment added successfully to page ID {page_id}.",
184
+ structured_content={
185
+ "comment_id": comment_response.comment_id,
186
+ "page_id": page_id,
187
+ },
188
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datarobot-genai
3
- Version: 0.2.16
3
+ Version: 0.2.17
4
4
  Summary: Generic helpers for GenAI
5
5
  Project-URL: Homepage, https://github.com/datarobot-oss/datarobot-genai
6
6
  Author: DataRobot, Inc.
@@ -76,11 +76,11 @@ datarobot_genai/drmcp/test_utils/utils.py,sha256=esGKFv8aO31-Qg3owayeWp32BYe1CdY
76
76
  datarobot_genai/drmcp/tools/__init__.py,sha256=0kq9vMkF7EBsS6lkEdiLibmUrghTQqosHbZ5k-V9a5g,578
77
77
  datarobot_genai/drmcp/tools/clients/__init__.py,sha256=0kq9vMkF7EBsS6lkEdiLibmUrghTQqosHbZ5k-V9a5g,578
78
78
  datarobot_genai/drmcp/tools/clients/atlassian.py,sha256=__M_uz7FrcbKCYRzeMn24DCEYD6OmFx_LuywHCxgXsA,6472
79
- datarobot_genai/drmcp/tools/clients/confluence.py,sha256=DF6TIGJfR3Lh-D_x66cDNkvOTS8gxL6bVhHRtcP0LKw,10493
79
+ datarobot_genai/drmcp/tools/clients/confluence.py,sha256=gDzy8t5t3b1mwEr-CuZ5BwXXQ52AXke8J_Ra7i_8T1g,13692
80
80
  datarobot_genai/drmcp/tools/clients/jira.py,sha256=Rm91JAyrNIqxu66-9rU1YqoRXVnWbEy-Ahvy6f6HlVg,9823
81
81
  datarobot_genai/drmcp/tools/clients/s3.py,sha256=GmwzvurFdNfvxOooA8g5S4osRysHYU0S9ypg_177Glg,953
82
82
  datarobot_genai/drmcp/tools/confluence/__init__.py,sha256=0kq9vMkF7EBsS6lkEdiLibmUrghTQqosHbZ5k-V9a5g,578
83
- datarobot_genai/drmcp/tools/confluence/tools.py,sha256=iqX7CR57WCXsQxHQCsfPL_Q78QjN9YZv3uIQbTMfYAg,5459
83
+ datarobot_genai/drmcp/tools/confluence/tools.py,sha256=jSF7yXGFqqlMcavkRIY4HbMxb7tCeunA2ST41wa2vGI,7219
84
84
  datarobot_genai/drmcp/tools/jira/__init__.py,sha256=0kq9vMkF7EBsS6lkEdiLibmUrghTQqosHbZ5k-V9a5g,578
85
85
  datarobot_genai/drmcp/tools/jira/tools.py,sha256=dfkqTU2HH-7n44hX80ODFacKq0p0LOchFcZtIIKFNMM,9687
86
86
  datarobot_genai/drmcp/tools/predictive/__init__.py,sha256=WuOHlNNEpEmcF7gVnhckruJRKU2qtmJLE3E7zoCGLDo,1030
@@ -106,9 +106,9 @@ datarobot_genai/nat/datarobot_llm_clients.py,sha256=Yu208Ed_p_4P3HdpuM7fYnKcXtim
106
106
  datarobot_genai/nat/datarobot_llm_providers.py,sha256=aDoQcTeGI-odqydPXEX9OGGNFbzAtpqzTvHHEkmJuEQ,4963
107
107
  datarobot_genai/nat/datarobot_mcp_client.py,sha256=35FzilxNp4VqwBYI0NsOc91-xZm1C-AzWqrOdDy962A,9612
108
108
  datarobot_genai/nat/helpers.py,sha256=Q7E3ADZdtFfS8E6OQPyw2wgA6laQ58N3bhLj5CBWwJs,3265
109
- datarobot_genai-0.2.16.dist-info/METADATA,sha256=lTI4a5PfXD7nIUcmAmzENdRiaIfgdaLoruSwq95NYU8,6301
110
- datarobot_genai-0.2.16.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
111
- datarobot_genai-0.2.16.dist-info/entry_points.txt,sha256=jEW3WxDZ8XIK9-ISmTyt5DbmBb047rFlzQuhY09rGrM,284
112
- datarobot_genai-0.2.16.dist-info/licenses/AUTHORS,sha256=isJGUXdjq1U7XZ_B_9AH8Qf0u4eX0XyQifJZ_Sxm4sA,80
113
- datarobot_genai-0.2.16.dist-info/licenses/LICENSE,sha256=U2_VkLIktQoa60Nf6Tbt7E4RMlfhFSjWjcJJfVC-YCE,11341
114
- datarobot_genai-0.2.16.dist-info/RECORD,,
109
+ datarobot_genai-0.2.17.dist-info/METADATA,sha256=FAAExscPOUIc1_sTNKY3DPTygcVtfrql4fubM9b6nDg,6301
110
+ datarobot_genai-0.2.17.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
111
+ datarobot_genai-0.2.17.dist-info/entry_points.txt,sha256=jEW3WxDZ8XIK9-ISmTyt5DbmBb047rFlzQuhY09rGrM,284
112
+ datarobot_genai-0.2.17.dist-info/licenses/AUTHORS,sha256=isJGUXdjq1U7XZ_B_9AH8Qf0u4eX0XyQifJZ_Sxm4sA,80
113
+ datarobot_genai-0.2.17.dist-info/licenses/LICENSE,sha256=U2_VkLIktQoa60Nf6Tbt7E4RMlfhFSjWjcJJfVC-YCE,11341
114
+ datarobot_genai-0.2.17.dist-info/RECORD,,