Skip to content

Commit

Permalink
Add support for vila-m3
Browse files Browse the repository at this point in the history
  • Loading branch information
adiazpinto committed Jan 28, 2025
1 parent cb20d90 commit c0a5674
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 13 deletions.
113 changes: 107 additions & 6 deletions plugins/RadCoPilot_Slicer/RadCoPilot.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,23 @@ def __init__(self, parent):
groupLayout.addRow(_("Server address:"), serverUrl)
parent.registerProperty("RadCoPilot/serverUrl", serverUrl, "text", str(qt.SIGNAL("textChanged(QString)")))

scanUrl = qt.QLineEdit()
groupLayout.addRow(_("Scan address:"), scanUrl)
parent.registerProperty("RadCoPilot/scanUrl", scanUrl, "text", str(qt.SIGNAL("textChanged(QString)")))


serverUrlHistory = qt.QLineEdit()
groupLayout.addRow(_("Server address history:"), serverUrlHistory)
parent.registerProperty(
"RadCoPilot/serverUrlHistory", serverUrlHistory, "text", str(qt.SIGNAL("textChanged(QString)"))
)

scanUrlHistory = qt.QLineEdit()
groupLayout.addRow(_("Scan address history:"), scanUrlHistory)
parent.registerProperty(
"RadCoPilot/scanUrlHistory", scanUrlHistory, "text", str(qt.SIGNAL("textChanged(QString)"))
)

fileExtension = qt.QLineEdit()
fileExtension.setText(".nii.gz")
fileExtension.toolTip = _("Default extension for uploading volumes")
Expand Down Expand Up @@ -147,9 +158,12 @@ def setup(self):
# in batch mode, without a graphical user interface.
self.tmpdir = slicer.util.tempDirectory("slicer-radcopilot")
self.logic = RadCoPilotLogic()
self.ui.scanComboBox.setEditable(True)
self.ui.serverComboBox.setEditable(True)

# Set icons and tune widget properties
self.ui.serverComboBox.lineEdit().setPlaceholderText("enter server address or leave empty to use default")
self.ui.scanComboBox.lineEdit().setPlaceholderText("enter scan address")
self.ui.fetchServerInfoButton.setIcon(self.icon("refresh-icon.png"))
self.ui.uploadImageButton.setIcon(self.icon("upload.svg"))

Expand All @@ -165,6 +179,9 @@ def setup(self):
self.ui.cleanOutputButton.connect("clicked(bool)", self.onClickCleanOutputButton)
self.ui.uploadImageButton.connect("clicked(bool)", self.onUploadImage)

self.updateServerUrlGUIFromSettings()
self.updateScanUrlGUIFromSettings()

def icon(self, name="RadCoPilot.png"):
'''Get the icon for the RadCoPilot module.'''
# It should not be necessary to modify this method
Expand All @@ -178,13 +195,49 @@ def updateServerSettings(self):
self.logic.setServer(self.serverUrl())
self.saveServerUrl()

def updateScanSettings(self):
'''Update the scan settings based on the current UI state.'''
self.saveScanUrl()

def serverUrl(self):
'''Get the current server URL from the UI.'''
serverUrl = self.ui.serverComboBox.currentText.strip()
if not serverUrl:
serverUrl = "http://localhost:8000"
# return serverUrl.rstrip("/")
return serverUrl

def scanUrl(self):
'''Get the current scan URL from the UI.'''
scanUrl = self.ui.scanComboBox.currentText.strip()
if not scanUrl:
print("Scan address is empty ... ")
return None
return scanUrl

def updateServerUrlGUIFromSettings(self):
# Save current server URL to the top of history
settings = qt.QSettings()
serverUrlHistory = settings.value("RadCoPilot/serverUrlHistory")

wasBlocked = self.ui.serverComboBox.blockSignals(True)
self.ui.serverComboBox.clear()
if serverUrlHistory:
self.ui.serverComboBox.addItems(serverUrlHistory.split(";"))
self.ui.serverComboBox.setCurrentText(settings.value("RadCoPilot/serverUrl"))
self.ui.serverComboBox.blockSignals(wasBlocked)

def updateScanUrlGUIFromSettings(self):
# Save current scan URL to the top of history
settings = qt.QSettings()
scanUrlHistory = settings.value("RadCoPilot/scanUrlHistory")

wasBlocked = self.ui.scanComboBox.blockSignals(True)
self.ui.scanComboBox.clear()
if scanUrlHistory:
self.ui.scanComboBox.addItems(scanUrlHistory.split(";"))
self.ui.scanComboBox.setCurrentText(settings.value("RadCoPilot/scanUrl"))
self.ui.scanComboBox.blockSignals(wasBlocked)

def saveServerUrl(self):
'''Save the current server URL to settings and update history.'''
Expand All @@ -210,7 +263,34 @@ def saveServerUrl(self):
serverUrlHistory = serverUrlHistory[:10] # keep up to first 10 elements
settings.setValue("RadCoPilot/serverUrlHistory", ";".join(serverUrlHistory))

# self.updateServerUrlGUIFromSettings()
self.updateServerUrlGUIFromSettings()


def saveScanUrl(self):
'''Save the current scan URL to settings and update history.'''
# self.updateParameterNodeFromGUI()

# Save selected server URL
settings = qt.QSettings()
scanUrl = self.ui.scanComboBox.currentText
settings.setValue("RadCoPilot/scanUrl", scanUrl)

# Save current scan URL to the top of history
scanUrlHistory = settings.value("RadCoPilot/serverUrlHistory")
if scanUrlHistory:
scanUrlHistory = scanUrlHistory.split(";")
else:
scanUrlHistory = []
try:
scanUrlHistory.remove(scanUrl)
except ValueError:
pass

scanUrlHistory.insert(0, scanUrl)
scanUrlHistory = scanUrlHistory[:10] # keep up to first 10 elements
settings.setValue("RadCoPilot/scanUrlHistory", ";".join(scanUrlHistory))

self.updateScanUrlGUIFromSettings()

def show_popup(self, title, message):
'''Display a popup message box with the given title and message.'''
Expand All @@ -225,6 +305,7 @@ def onClickFetchInfo(self):

try:
self.updateServerSettings()
self.updateScanSettings()
info = self.logic.info()
self.info = info

Expand All @@ -249,14 +330,32 @@ def onClickFetchInfo(self):
def onClickCleanOutputButton(self):
'''Handle the click event for cleaning the output text.'''
self.ui.outputText.clear()

def onUploadImage(self):
'''Gets the volume and sen it to the server.'''
volumeNode = slicer.mrmlScene.GetFirstNodeByClass("vtkMRMLScalarVolumeNode")
image_id = volumeNode.GetName()

'''Gets the text from scanComboBox or the volume from the viewport and sends it to the server.'''
try:
qt.QApplication.setOverrideCursor(qt.Qt.WaitCursor)

# Check if there's text in the scanComboBox
scan_text = self.ui.scanComboBox.currentText
if scan_text:
# Send the text directly
info = self.logic.uploadScan(scan_text)
self.info = info
print(f"Response from the upload text call: {self.info['status']}")
self.reportProgress(100)
qt.QApplication.restoreOverrideCursor()
self.show_popup("Information", "Text uploaded")
return True

# If no text, check for a volume in the viewport
volumeNode = slicer.mrmlScene.GetFirstNodeByClass("vtkMRMLScalarVolumeNode")
if not volumeNode:
qt.QApplication.restoreOverrideCursor()
self.show_popup("Error", "No text in scanComboBox and no volume in viewport")
return False

image_id = volumeNode.GetName()
in_file = tempfile.NamedTemporaryFile(suffix=self.file_ext, dir=self.tmpdir).name
self.current_sample = in_file
self.reportProgress(5)
Expand All @@ -279,8 +378,10 @@ def onUploadImage(self):
msg = f"Message: {e.msg}" if hasattr(e, "msg") else ""
self.reportProgress(100)
qt.QApplication.restoreOverrideCursor()
self.show_popup("Error", f"Upload failed: {msg}")
return False


def reportProgress(self, progressPercentage):
'''Reports progress of an event.'''
if not self.progressBar:
Expand Down
20 changes: 14 additions & 6 deletions plugins/RadCoPilot_Slicer/RadCoPilotLib/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,15 +77,15 @@ def uploadFile(self, volumePath):
"""
Upload a file to the RadCoPilot server using the fileUploadRouter.
This method sends a file to the '/upload' endpoint of the RadCoPilot server,
This method sends either a local file or a URL to the '/upload' endpoint of the RadCoPilot server,
which stores it as the last received file. This uploaded file can then be
used in subsequent requests to the chat_completions API if no file is
provided in those requests.
Parameters:
-----------
filePath : str
The path to the file that should be uploaded to the server.
volumePath : str
The path to the local file or a URL that should be uploaded to the server.
Returns:
--------
Expand All @@ -103,13 +103,21 @@ def uploadFile(self, volumePath):
print("Uploading file...")

selector = "/upload/"

url = f"{self._server_url}{selector}"

# Check if volumePath is a URL
parsed_url = urlparse(volumePath)
is_url = bool(parsed_url.scheme and parsed_url.netloc)

with open(volumePath, 'rb') as file:
files = {"file": (os.path.basename(volumePath), file, "application/octet-stream")}
if is_url:
# If it's a URL, send it as a form-data field
files = {"file": (None, str(volumePath))}
response = requests.post(url=url, files=files)
else:
# If it's a local file, open and send it
with open(volumePath, 'rb') as file:
files = {"file": (os.path.basename(volumePath), file, "application/octet-stream")}
response = requests.post(url=url, files=files)

if response.status_code == 200:
return response.json()
Expand Down
12 changes: 11 additions & 1 deletion plugins/RadCoPilot_Slicer/Resources/UI/RadCoPilot.ui
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,23 @@
</item>
<item>
<layout class="QGridLayout" name="configGrid">
<item row="5" column="0">
<item row="6" column="0">
<widget class="QPushButton" name="uploadImageButton">
<property name="text">
<string>Submit scan</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Scan address</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QComboBox" name="scanComboBox"/>
</item>
</layout>
</item>
<item>
Expand Down

0 comments on commit c0a5674

Please sign in to comment.