Skip to content

Conversation

@LucaVor
Copy link
Collaborator

@LucaVor LucaVor commented Nov 23, 2025

This pull request introduces significant updates to the Mantis SDK, focusing on improved authentication, notebook management, and enhanced documentation and examples. The most notable changes are the requirement of a cookie and space_id for client authentication, the addition of notebook management methods, and corresponding updates to the documentation and example notebooks.

Authentication and Client Initialization:

  • The MantisClient now requires both a cookie and a space_id upon initialization. These are used to authenticate the user and obtain a VSCode token for API requests. The authentication process is implemented within the client, and the VSCode token is included in subsequent requests for improved security and compatibility. [1] [2] [3] [4] [5]

Notebook Management:

  • New methods have been added to MantisClient for creating and retrieving notebooks within a space: create_notebook and get_notebook. These facilitate programmatic notebook management and are documented in both the SDK and example notebooks. [1] [2] [3]

Documentation and Example Updates:

  • The README.md and examples/demo.ipynb have been updated to reflect the new authentication requirements and demonstrate notebook creation and execution. These changes help users understand how to use the updated SDK features, including configuring the client and working with notebooks. [1] [2] [3] [4] [5] [6] [7] [8] [9]

Additional API Utility:

  • Added a new resolve_map_to_project method to MantisClient to resolve a map ID to a project ID, supporting more flexible project management workflows.

These changes collectively enhance the SDK's usability, security, and documentation, making it easier for users to authenticate, configure, and manage their workspaces and notebooks programmatically.

@review-notebook-app
Copy link

Check out this pull request on  ReviewNB

See visual diffs & provide feedback on Jupyter Notebooks.


Powered by ReviewNB

@gemini-code-assist
Copy link

Summary of Changes

Hello @LucaVor, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly upgrades the Mantis SDK by introducing a more robust authentication mechanism that leverages cookies and space IDs to generate VSCode tokens. It also adds comprehensive capabilities for managing notebooks programmatically, allowing users to create, retrieve, and interact with notebook cells directly through the SDK. These changes are supported by updated documentation and examples, enhancing the SDK's usability and security for developers.

Highlights

  • Enhanced Authentication: The MantisClient now requires both a 'cookie' and a 'space_id' for initialization, implementing a new authentication flow to obtain and utilize a VSCode token for API requests, thereby improving security and compatibility.
  • Notebook Management: New methods 'create_notebook' and 'get_notebook' have been added to the MantisClient, along with new 'Notebook' and 'Cell' classes, enabling programmatic creation, retrieval, and interaction with notebooks and their individual cells (add, update, delete, execute).
  • Updated Documentation and Examples: The 'README.md' and 'examples/demo.ipynb' have been thoroughly updated to reflect the new authentication requirements, demonstrate notebook creation and execution, and showcase client configuration options.
  • Modular Space Initialization: The 'Space' object's initialization now supports a configurable 'wait_for' property, allowing for more flexible control over when the space is considered loaded.
  • New API Utility: A 'resolve_map_to_project' method has been added to the MantisClient, facilitating the resolution of a map ID to a project ID for more flexible project management workflows.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces significant updates to the Mantis SDK, focusing on notebook management and revised authentication. While the new features are a great addition, I've found a few critical issues in the client's networking logic that will likely cause all API requests to fail due to improper URL construction. There is also a high-severity issue in the new notebook functionality that could lead to an infinite loop. I've provided detailed comments and code suggestions to address these problems. Additionally, there are some medium-severity issues related to exception handling, a typo, and clarity in the documentation examples.

Comment on lines +73 to +96
def _authenticate(self):
"""
Authenticates by creating a VSCode token.
"""
try:
# We use the cookie to get the token
# The endpoint is /api/jupyter/vscode-auth-token/
# It requires project_id in the body
url = f"{self.base_url.lstrip('/').rstrip('/')}/api/jupyter/vscode-auth-token/"
headers = {"cookie": self.cookie}
data = {"project_id": self.space_id}

response = requests.post(url, json=data, headers=headers)
response.raise_for_status()
result = response.json()

if result.get("success") and result.get("token"):
self.vscode_token = result.get("token")
logger.info("Successfully authenticated with VSCode token.")
else:
logger.warning(f"Failed to get VSCode token: {result}")

except Exception as e:
logger.error(f"Authentication failed: {e}")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The _authenticate method has a couple of issues:

  1. It constructs a relative URL and uses requests.post directly, which will fail. It should use self._request to ensure the correct full URL is used and to centralize request logic.
  2. It catches a broad Exception, which can hide other bugs.

Here is a suggested refactoring that addresses both points by using the self._request method and catching a more specific exception.

Suggested change
def _authenticate(self):
"""
Authenticates by creating a VSCode token.
"""
try:
# We use the cookie to get the token
# The endpoint is /api/jupyter/vscode-auth-token/
# It requires project_id in the body
url = f"{self.base_url.lstrip('/').rstrip('/')}/api/jupyter/vscode-auth-token/"
headers = {"cookie": self.cookie}
data = {"project_id": self.space_id}
response = requests.post(url, json=data, headers=headers)
response.raise_for_status()
result = response.json()
if result.get("success") and result.get("token"):
self.vscode_token = result.get("token")
logger.info("Successfully authenticated with VSCode token.")
else:
logger.warning(f"Failed to get VSCode token: {result}")
except Exception as e:
logger.error(f"Authentication failed: {e}")
def _authenticate(self):
"""
Authenticates by creating a VSCode token.
"""
try:
data = {"project_id": self.space_id}
result = self._request("POST", "/api/jupyter/vscode-auth-token/", json=data)
if result.get("success") and result.get("token"):
self.vscode_token = result.get("token")
logger.info("Successfully authenticated with VSCode token.")
else:
logger.warning(f"Failed to get VSCode token: {result}")
except RuntimeError as e:
logger.error(f"Authentication failed: {e}")

return s.lstrip('/').rstrip('/')

url = f"{self.config.host}/{remove_slash(self.base_url)}/{remove_slash(endpoint)}/"
url = f"{remove_slash(self.base_url)}/{remove_slash(endpoint)}/"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The self.config.host component has been removed from the URL construction. This will result in relative URL paths being passed to requests, causing API calls to fail. This should be reverted to include the host, as in the previous version.

Suggested change
url = f"{remove_slash(self.base_url)}/{remove_slash(endpoint)}/"
url = f"{self.config.host}/{remove_slash(self.base_url)}/{remove_slash(endpoint)}/"

Comment on lines +239 to +253
while True:
time.sleep(0.5)
self._refresh_content()

if index >= len(self.cells):
logger.warning(f"Cell index {index} out of range (cells count: {len(self.cells)}). Waiting for refresh...")
continue

cell = self.cells[index]
# check if executing
# note: metadata might be None
meta = cell.metadata or {}
if not meta.get("executing", False):
# execution finished
return cell.outputs

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This while True loop polls for cell execution completion but lacks a timeout. If the backend process hangs or fails to update the 'executing' status, this will result in an infinite loop on the client side. You should add a timeout to prevent this, potentially using the timeout value from the client configuration.

Suggested change
while True:
time.sleep(0.5)
self._refresh_content()
if index >= len(self.cells):
logger.warning(f"Cell index {index} out of range (cells count: {len(self.cells)}). Waiting for refresh...")
continue
cell = self.cells[index]
# check if executing
# note: metadata might be None
meta = cell.metadata or {}
if not meta.get("executing", False):
# execution finished
return cell.outputs
start_time = time.time()
timeout_seconds = self.client.config.timeout / 1000 # config.timeout is in ms
while time.time() - start_time < timeout_seconds:
time.sleep(0.5)
self._refresh_content()
if index >= len(self.cells):
logger.warning(f"Cell index {index} out of range (cells count: {len(self.cells)}). Waiting for refresh...")
continue
cell = self.cells[index]
# check if executing
# note: metadata might be None
meta = cell.metadata or {}
if not meta.get("executing", False):
# execution finished
return cell.outputs
raise RuntimeError(f"Cell execution timed out after {timeout_seconds} seconds.")


```python
# Create a notebook
nb = mantis.create_notebook(space_id, "My Analysis", "user_id")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

In this example snippet, the space_id variable is used but it has not been defined within this context. To make the example runnable and clearer for users, you should use new_space_id, which is created in the "Quick Start" example earlier in the document.

Suggested change
nb = mantis.create_notebook(space_id, "My Analysis", "user_id")
nb = mantis.create_notebook(new_space_id, "My Analysis", "user_id")

"render_args": RenderArgs(headless=True)
})

mantis = MantisClient("/api/proxy/", cookie=cookie, space_id=space_id, config=config)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The space_id variable used here is not defined in the example. To improve clarity, you should either use new_space_id from the previous examples or a placeholder string like "YOUR_SPACE_ID_HERE".

Suggested change
mantis = MantisClient("/api/proxy/", cookie=cookie, space_id=space_id, config=config)
mantis = MantisClient("/api/proxy/", cookie=cookie, space_id=new_space_id, config=config)

Comment on lines +133 to +143
reponse_content = None

try:
response = requests.request(method, url, headers=headers, **kwargs)
reponse_content = response.text
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
raise RuntimeError(f"API request failed: {e}. Text: {response.text}")
# capture response text for better debugging
response_text = reponse_content if reponse_content else getattr(e.response, 'text', '') if e.response else str(e)
raise RuntimeError(f"API request failed: {e}. Text: {response_text}\nURL: {url}\nHeaders: {headers}")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There is a typo in the variable name reponse_content. It should be response_content for consistency and correctness. This typo appears on lines 133, 137, and 142.

Suggested change
reponse_content = None
try:
response = requests.request(method, url, headers=headers, **kwargs)
reponse_content = response.text
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
raise RuntimeError(f"API request failed: {e}. Text: {response.text}")
# capture response text for better debugging
response_text = reponse_content if reponse_content else getattr(e.response, 'text', '') if e.response else str(e)
raise RuntimeError(f"API request failed: {e}. Text: {response_text}\nURL: {url}\nHeaders: {headers}")
response_content = None
try:
response = requests.request(method, url, headers=headers, **kwargs)
response_content = response.text
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
# capture response text for better debugging
response_text = response_content if response_content else getattr(e.response, 'text', '') if e.response else str(e)
raise RuntimeError(f"API request failed: {e}. Text: {response_text}\nURL: {url}\nHeaders: {headers}")

response = self.client._request("POST", "/api/sessions/check", json={"session_id": self.session_id, "project_id": self.space_id})
if response.get("success"):
return
except Exception:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Catching a bare Exception is too broad and can hide unrelated bugs. It's better to catch a more specific exception that you expect here, for example RuntimeError which can be raised by self.client._request.

Suggested change
except Exception:
except RuntimeError:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants