From b849ec1b07aaa077341c383f157823594bbb9dac Mon Sep 17 00:00:00 2001 From: Joshua Villalta <93329405+vgoat21@users.noreply.github.com> Date: Mon, 13 Jul 2026 16:56:51 +0100 Subject: [PATCH 1/2] fix: remove legacy similarity functionality --- reai_toolkit/app/app.py | 4 - .../app/components/tabs/similarity_tab.py | 322 ------------------ reai_toolkit/app/coordinator.py | 10 - .../app/coordinators/matching_coordinator.py | 1 - .../coordinators/similarity_coordinator.py | 181 ---------- .../services/matching/similarity_service.py | 116 ------- reai_toolkit/hooks/popup.py | 36 -- 7 files changed, 670 deletions(-) delete mode 100644 reai_toolkit/app/components/tabs/similarity_tab.py delete mode 100644 reai_toolkit/app/coordinators/similarity_coordinator.py delete mode 100644 reai_toolkit/app/services/matching/similarity_service.py diff --git a/reai_toolkit/app/app.py b/reai_toolkit/app/app.py index e1d2733..9f77cd9 100644 --- a/reai_toolkit/app/app.py +++ b/reai_toolkit/app/app.py @@ -16,7 +16,6 @@ from reai_toolkit.app.services.rename.rename_service import RenameService from reai_toolkit.app.services.upload.upload_service import UploadService from reai_toolkit.app.services.data_types.data_types_service import ImportDataTypesService -from reai_toolkit.app.services.matching.similarity_service import SimilarityService from reai_toolkit.app.services.variable_sync.variable_sync_service import VariableSyncService @@ -71,9 +70,6 @@ def __init__(self, ida_version: str = "UNKNOWN", plugin_version: str = "UNKNOWN" self.matching_service = MatchingService( netstore_service=self.netstore_service, sdk_config=sdk_config ) - self.similarity_service = SimilarityService( - netstore_service=self.netstore_service, sdk_config=sdk_config - ) self.chat_service = ChatService( netstore_service=self.netstore_service, sdk_config=sdk_config ) diff --git a/reai_toolkit/app/components/tabs/similarity_tab.py b/reai_toolkit/app/components/tabs/similarity_tab.py deleted file mode 100644 index c5ccaad..0000000 --- a/reai_toolkit/app/components/tabs/similarity_tab.py +++ /dev/null @@ -1,322 +0,0 @@ -from enum import IntEnum -from typing import Any, Optional, Callable - -import idaapi -import ida_kernwin as kw -from loguru import logger - -from revengai.models.matched_function import MatchedFunction - -from reai_toolkit.app.core.qt_compat import QtCore, QtGui, QtWidgets, Signal - - -TAB_TITLE = "RevEng.AI — Function Similarity" - - -class SimilarityTableColumns(IntEnum): - FUNCTION = 0 - SIMILARITY = 1 - CONFIDENCE = 2 - BINARY = 3 - DIFF = 4 - - -class ButtonDelegate(QtWidgets.QStyledItemDelegate): - clicked = Signal(QtCore.QModelIndex) - - def __init__(self, parent=None) -> None: - super().__init__(parent) - self._pressed_index = None - - def paint( - self, - painter: QtGui.QPainter, - option: QtWidgets.QStyleOptionViewItem, - index: QtCore.QModelIndex, - ) -> None: - button_option = QtWidgets.QStyleOptionButton() - button_option.rect = option.rect.adjusted(4, 4, -4, -4) - button_option.text = "View" - button_option.state = QtWidgets.QStyle.State_Enabled - - if self._pressed_index == index: - button_option.state |= QtWidgets.QStyle.State_Sunken - - QtWidgets.QApplication.style().drawControl( - QtWidgets.QStyle.CE_PushButton, button_option, painter - ) - - def editorEvent( - self, - event: QtCore.QEvent, - model: QtCore.QAbstractItemModel, - option: QtWidgets.QStyleOptionViewItem, - index: QtCore.QModelIndex, - ) -> bool: - if event.type() == QtCore.QEvent.Type.MouseButtonPress: - self._pressed_index = index - return True - elif event.type() == QtCore.QEvent.Type.MouseButtonRelease: - if self._pressed_index == index: - self.clicked.emit(index) - self._pressed_index = None - return True - return False - - def sizeHint( - self, option: QtWidgets.QStyleOptionViewItem, index: QtCore.QModelIndex - ) -> QtCore.QSize: - return QtCore.QSize(60, 30) - - -class SimilarityTableModel(QtCore.QAbstractTableModel): - COLUMNS: list[str] = ["Function", "Similarity", "Confidence", "Binary", ""] - - def __init__(self, parent=None) -> None: - super().__init__(parent) - self._data: list[MatchedFunction] = [] - self._func_id: int | None = None - self._analysis_id: int | None = None - self._portal_url: str = "https://portal.reveng.ai" - - def rowCount(self, parent=QtCore.QModelIndex()) -> int: - return len(self._data) - - def columnCount(self, parent=QtCore.QModelIndex()) -> int: - return len(self.COLUMNS) - - def data( - self, index: QtCore.QModelIndex, role: int = QtCore.Qt.ItemDataRole.DisplayRole - ) -> Any: - if not index.isValid() or not (0 <= index.row() < len(self._data)): - return None - - match: MatchedFunction = self._data[index.row()] - col: int = index.column() - - if role == QtCore.Qt.ItemDataRole.DisplayRole: - if col == SimilarityTableColumns.FUNCTION: - return match.function_name - elif col == SimilarityTableColumns.SIMILARITY: - similarity: float = match.similarity or 0.0 - return f"{similarity:.1f}%" - elif col == SimilarityTableColumns.CONFIDENCE: - confidence: float = match.confidence or 0.0 - return f"{confidence:.1f}%" - elif col == SimilarityTableColumns.BINARY: - return match.binary_name - elif col == SimilarityTableColumns.DIFF: - return None # Button column - handled by delegate - - elif role == QtCore.Qt.ItemDataRole.UserRole: - return f"{self._portal_url}/analyses/{self._analysis_id}?fn={self._func_id}&view=matching&matchingMode=single&alt={match.function_id}" - - elif role == QtCore.Qt.ItemDataRole.TextAlignmentRole: - if col == SimilarityTableColumns.SIMILARITY: - return QtCore.Qt.AlignmentFlag.AlignCenter - return ( - QtCore.Qt.AlignmentFlag.AlignLeft | QtCore.Qt.AlignmentFlag.AlignVCenter - ) - - elif role == QtCore.Qt.ItemDataRole.ForegroundRole: - if col == SimilarityTableColumns.SIMILARITY: - similarity = match.similarity or 0 - if similarity >= 90: - return QtGui.QColor("#2ecc71") # Green - elif similarity >= 70: - return QtGui.QColor("#f39c12") # Orange - else: - return QtGui.QColor("#e74c3c") # Red - elif col == SimilarityTableColumns.CONFIDENCE: - confidence = match.confidence or 0 - if confidence >= 90: - return QtGui.QColor("#2ecc71") # Green - elif confidence >= 70: - return QtGui.QColor("#f39c12") # Orange - else: - return QtGui.QColor("#e74c3c") # Red - - return None - - def headerData( - self, - section: int, - orientation: QtCore.Qt.Orientation, - role: int = QtCore.Qt.ItemDataRole.DisplayRole, - ) -> Any: - if ( - orientation == QtCore.Qt.Orientation.Horizontal - and role == QtCore.Qt.ItemDataRole.DisplayRole - ): - return self.COLUMNS[section] - return None - - def set_data(self, func_id: int, data: list[MatchedFunction], analysis_id: int, portal_url: str) -> None: - self.beginResetModel() - self._func_id = func_id - self._data = data - self._analysis_id = analysis_id - self._portal_url = portal_url - self.endResetModel() - - def clear(self) -> None: - self.beginResetModel() - self._data = [] - self.endResetModel() - - -class SimilarityTab(kw.PluginForm): - """Dockable view showing similar functions for the currently selected function.""" - - def __init__(self, on_close_callback: Callable) -> None: - super().__init__() - self._parent_w: Optional[QtWidgets.QWidget] = None - self._table: Optional[QtWidgets.QTableView] = None - self._model: Optional[SimilarityTableModel] = None - self._status_label: Optional[QtWidgets.QLabel] = None - self._current_func_addr: Optional[int] = None - self._current_func_id: Optional[int] = None - self._on_close_callback = on_close_callback - - def Create(self, title: str = "") -> Any: - flags = getattr(kw.PluginForm, "WOPN_DP_TAB", 0) | getattr( - kw.PluginForm, "WOPN_RESTORE", 0 - ) - success = self.Show(str(title) if title else TAB_TITLE, flags) - if not success: - logger.error("Failed to show Function Similarity tab") - else: - try: - kw.set_dock_pos(TAB_TITLE, "Pseudocode-A", kw.DP_TAB) - except Exception as e: - logger.warning(f"Could not dock next to Pseudocode: {e}") - - return success - - def OnCreate(self, form) -> None: - self._parent_w = kw.PluginForm.FormToPyQtWidget(form) # type: ignore - - if self._parent_w is None: - return - - # Main layout - layout = QtWidgets.QVBoxLayout(self._parent_w) - layout.setContentsMargins(4, 4, 4, 4) - layout.setSpacing(4) - - # Header with status - header_layout = QtWidgets.QHBoxLayout() - self._status_label = QtWidgets.QLabel("Select a function to view similarities") - self._status_label.setStyleSheet("color: #888; font-style: italic;") - header_layout.addWidget(self._status_label) - header_layout.addStretch() - - layout.addLayout(header_layout) - - # Table view - self._table = QtWidgets.QTableView(self._parent_w) - self._model = SimilarityTableModel(self._table) - self._table.setModel(self._model) - - # Configure table appearance - self._table.setSelectionBehavior( - QtWidgets.QAbstractItemView.SelectionBehavior.SelectRows - ) - self._table.setSelectionMode( - QtWidgets.QAbstractItemView.SelectionMode.SingleSelection - ) - self._table.setAlternatingRowColors(True) - self._table.verticalHeader().setVisible(False) - self._table.horizontalHeader().setStretchLastSection(False) - self._table.setShowGrid(False) - - # Set column widths - header: QtWidgets.QHeaderView = self._table.horizontalHeader() - header.setSectionResizeMode( - SimilarityTableColumns.FUNCTION, QtWidgets.QHeaderView.ResizeMode.Stretch - ) - header.setSectionResizeMode( - SimilarityTableColumns.SIMILARITY, QtWidgets.QHeaderView.ResizeMode.Fixed - ) - header.setSectionResizeMode( - SimilarityTableColumns.BINARY, QtWidgets.QHeaderView.ResizeMode.Stretch - ) - header.setSectionResizeMode( - SimilarityTableColumns.DIFF, QtWidgets.QHeaderView.ResizeMode.Fixed - ) - self._table.setColumnWidth(SimilarityTableColumns.SIMILARITY, 80) - self._table.setColumnWidth(SimilarityTableColumns.DIFF, 70) - - # Button delegate for the last column - button_delegate = ButtonDelegate(self._table) - button_delegate.clicked.connect(self._on_button_clicked) - self._table.setItemDelegateForColumn( - SimilarityTableColumns.DIFF, button_delegate - ) - - layout.addWidget(self._table) - - def OnClose(self, form) -> None: - self._on_close_callback() - - def _on_button_clicked(self, index: QtCore.QModelIndex) -> None: - """Handle click on the View button.""" - if self._model is None: - return - - url = self._model.data(index, QtCore.Qt.ItemDataRole.UserRole) - if url: - logger.debug(f"Opening URL: {url}") - QtGui.QDesktopServices.openUrl(QtCore.QUrl(url)) - - def _on_fetch_finished( - self, func_id: int, func_addr: int, data: list[MatchedFunction], analysis_id: int, portal_url: str - ) -> None: - """Handle successful API response (called on main thread).""" - # Ignore results if user has moved to a different function - if func_addr != self._current_func_addr: - return - - if self._model: - self._model.set_data(func_id, data, analysis_id, portal_url) - - func_name = idaapi.get_func_name(func_addr) or f"sub_{func_addr:X}" - - if self._status_label: - if data: - self._status_label.setText( - f"Similar to {func_name} ({len(data)} results)" - ) - self._status_label.setStyleSheet("color: #ccc;") - else: - self._status_label.setText( - f"No similar functions found for {func_name}" - ) - self._status_label.setStyleSheet("color: #888; font-style: italic;") - - def update_for_function( - self, - func_id: int, - func_addr: int, - data: list[MatchedFunction], - analysis_id: int, - portal_url: str, - force: bool = False, - ) -> None: - if not force and func_addr == self._current_func_addr: - return - - self._current_func_addr = func_addr - - # Get function name for display - func_name: str = idaapi.get_func_name(func_addr) - - if self._status_label: - self._status_label.setText(f"Loading similarities for {func_name}...") - self._status_label.setStyleSheet("color: #888; font-style: italic;") - - # Clear table while loading - if self._model: - self._model.clear() - - self._on_fetch_finished(func_id, func_addr, data, analysis_id, portal_url) diff --git a/reai_toolkit/app/coordinator.py b/reai_toolkit/app/coordinator.py index 6ddb27c..33d3ac3 100644 --- a/reai_toolkit/app/coordinator.py +++ b/reai_toolkit/app/coordinator.py @@ -24,9 +24,6 @@ from reai_toolkit.app.coordinators.sync_analysis_coordinator import ( AnalysisSyncCoordinator, ) -from reai_toolkit.app.coordinators.similarity_coordinator import ( - SimilarityCoordinator -) from reai_toolkit.app.core.qt_compat import QtCore, QtGui @@ -98,13 +95,6 @@ def __init__(self, app, factory, log): rename_service=app.rename_service, ) - self.similarityc: SimilarityCoordinator = SimilarityCoordinator( - app=app, - factory=factory, - log=log, - similarity_service=app.similarity_service - ) - self.chatc: ChatCoordinator = ChatCoordinator( app=app, factory=factory, diff --git a/reai_toolkit/app/coordinators/matching_coordinator.py b/reai_toolkit/app/coordinators/matching_coordinator.py index 49d89d4..b6255d3 100644 --- a/reai_toolkit/app/coordinators/matching_coordinator.py +++ b/reai_toolkit/app/coordinators/matching_coordinator.py @@ -5,7 +5,6 @@ from revengai import FunctionMapping from reai_toolkit.app.components.dialogs.matching_dialog import MatchingDialog -from reai_toolkit.app.components.tabs.similarity_tab import SimilarityTab from reai_toolkit.app.coordinators.base_coordinator import BaseCoordinator from reai_toolkit.app.services.matching.matching_service import MatchingService from reai_toolkit.app.services.matching.schema import ValidFunction diff --git a/reai_toolkit/app/coordinators/similarity_coordinator.py b/reai_toolkit/app/coordinators/similarity_coordinator.py deleted file mode 100644 index 9ae8d2a..0000000 --- a/reai_toolkit/app/coordinators/similarity_coordinator.py +++ /dev/null @@ -1,181 +0,0 @@ -import time -from typing import TYPE_CHECKING - -from loguru import logger -import idaapi -import ida_funcs -import ida_kernwin as kw - - -from reai_toolkit.app.components.tabs.similarity_tab import SimilarityTab -from reai_toolkit.app.coordinators.base_coordinator import BaseCoordinator -from reai_toolkit.app.services.matching.similarity_service import SimilarityService -from revengai.models.matched_function import MatchedFunction -from revengai import FunctionMapping - -if TYPE_CHECKING: - from reai_toolkit.app.app import App - from reai_toolkit.app.factory import DialogFactory - - -class SimilarityCoordinator(BaseCoordinator): - def __init__( - self, - *, - app: "App", - factory: "DialogFactory", - log, - similarity_service: SimilarityService, - ) -> None: - super().__init__(app=app, factory=factory, log=log) - - self._similarity_service: SimilarityService = similarity_service - self._similarity_tab: SimilarityTab | None = None - self._hook: FunctionSimilarityHook | None = None - - def find_similar_functions(self) -> None: - logger.debug("find_similar_functions called") - func_map: FunctionMapping | None = ( - self._similarity_service.netstore_service.get_function_mapping() - ) - if func_map is None: - return - - inverse_map: dict[str, int] = func_map.inverse_function_map - ea: int = kw.get_screen_ea() - current_func: ida_funcs.func_t | None = ida_funcs.get_func(ea) - if current_func is None: - return - - function_id: int | None = inverse_map.get(str(current_func.start_ea), None) - if function_id is None: - return - - callback = ( - self._launch_similarity_tab - if self._similarity_tab is None - else self._update_similarity_tab - ) - if self._similarity_service.is_worker_running() is False: - self._similarity_service.fetch_similar_functions(function_id, ea, callback) - - def enable_function_tracking(self) -> None: - if self._hook is None: - self._hook = FunctionSimilarityHook(self) # type: ignore - self._hook.hook() - - def disable_function_tracking(self) -> None: - if self._hook is not None: - self._hook.unhook() - self._hook = None - - def _launch_similarity_tab( - self, local_func_id: int, local_vaddr: int, functions: list[MatchedFunction], analysis_id: int - ) -> None: - def _show_tab() -> None: - self._similarity_tab = SimilarityTab(self._on_pane_closed) # type: ignore - self._similarity_tab.Create() - self._similarity_tab.update_for_function( - local_func_id, local_vaddr, functions, analysis_id, self.app.config_service.portal_url - ) - - kw.execute_ui_requests([_show_tab]) - - def _update_similarity_tab( - self, local_func_id: int, local_vaddr: int, functions: list[MatchedFunction], analysis_id: int - ) -> None: - def _update_tab() -> None: - if self._similarity_tab is None: - return - - self._similarity_tab.update_for_function( - local_func_id, local_vaddr, functions, analysis_id, self.app.config_service.portal_url - ) - - kw.execute_ui_requests([_update_tab]) - - def _on_pane_closed(self) -> None: - self._similarity_tab = None - self.disable_function_tracking() - - def run_dialog(self) -> None: - pass - - -class FunctionSimilarityHook(kw.UI_Hooks): - def __init__( - self, coordinator: SimilarityCoordinator, debounce_ms: int = 200 - ) -> None: - super().__init__() - self.coordinator: SimilarityCoordinator = coordinator - self._debounce_ms = debounce_ms - self._last_func_start = None - self._pending_ea = None - self._last_change_time = 0.0 - self._timer_id = None - self._is_hooked = False - - def hook(self) -> bool: - if self._is_hooked: - return False - ok = super().hook() - if ok: - # First hook, does not trigger event. Manual call to coordinator. - self._is_hooked = True - self.coordinator.find_similar_functions() - logger.info("[FunctionSimilarityHook] Hook registered.") - else: - logger.error("[FunctionSimilarityHook] Failed to register.") - return ok - - def unhook(self) -> None: - if self._timer_id: - idaapi.unregister_timer(self._timer_id) - self._timer_id = None - if self._is_hooked: - super().unhook() - self._is_hooked = False - logger.info("[FunctionSimilarityHook] Hook removed.") - self._pending_ea = None - self._last_func_start = None - - def screen_ea_changed(self, ea: int, prev_ea: int) -> None: - func: ida_funcs.func_t = ida_funcs.get_func(ea) - if not func: - return - - start = func.start_ea - if start == self._last_func_start: - return # same function, ignore - - self._pending_ea = start - self._last_change_time = time.time() - - if not self._timer_id: - self._timer_id = idaapi.register_timer( - self._debounce_ms, self._check_debounce - ) - - def _check_debounce(self): - now: float = time.time() - - # Wait until no changes for the debounce interval - if self._pending_ea and (now - self._last_change_time) >= ( - self._debounce_ms / 1000.0 - ): - ea = self._pending_ea - self._pending_ea = None - self._timer_id = None - self._last_func_start = ea - - logger.debug( - f"[FunctionSimilarityHook] Function changed (debounced): {hex(ea)}" - ) - - try: - self.coordinator.find_similar_functions() - except Exception as e: - logger.error(f"[FunctionSimilarityHook] Callback failed: {e}") - return -1 # stop timer - - return 1 # keep timer alive until debounce passes diff --git a/reai_toolkit/app/services/matching/similarity_service.py b/reai_toolkit/app/services/matching/similarity_service.py deleted file mode 100644 index 286bc72..0000000 --- a/reai_toolkit/app/services/matching/similarity_service.py +++ /dev/null @@ -1,116 +0,0 @@ -from typing import Callable -import threading -import time - -from loguru import logger -from revengai import ( - ApiException, - Configuration, - GetMatchesOutputBody, - GetMatchesStatusOutputBody, - StartMatchingForFunctionsInputBody, - TaskStatus, -) - -from reai_toolkit.app.core import SimpleNetStore -from reai_toolkit.app.interfaces.thread_service import IThreadService -from revengai.models.matched_function import MatchedFunction -from revengai.api.functions_core_api import FunctionsCoreApi - - -class SimilarityService(IThreadService): - def __init__( - self, netstore_service: SimpleNetStore, sdk_config: Configuration - ) -> None: - super().__init__(netstore_service=netstore_service, sdk_config=sdk_config) - - def fetch_similar_functions( - self, - func_id: int, - vaddr: int, - callback: Callable[[int, int, list[MatchedFunction], int], None], - ) -> None: - self.start_worker( - target=self._perform_function_similarity_request, - args=(func_id, vaddr, callback), - ) - - def _perform_function_similarity_request( - self, - stop_event: threading.Event, - func_id: int, - vaddr: int, - callback: Callable[[int, int, list[MatchedFunction], int], None], - ) -> None: - analysis_id: int | None = self.netstore_service.get_analysis_id() - if analysis_id is None: - logger.warning( - "failed to perform similarity request due to invalid analysis id" - ) - return - - with self.yield_api_client(sdk_config=self.sdk_config) as api_client: - api: FunctionsCoreApi = FunctionsCoreApi(api_client) - - try: - api.start_functions_matching( - StartMatchingForFunctionsInputBody( - function_ids=[func_id], - results_per_function=16, - min_similarity=70, - ) - ) - except ApiException as e: - logger.error( - f"failed to start similarity request due to {e}" - ) - return - - timeout: float = 60.0 - elapsed: float = 0.0 - sleep_interval: float = 0.5 - - while stop_event.is_set() is False and elapsed < timeout: - try: - status: GetMatchesStatusOutputBody = ( - api.get_functions_matching_status(function_ids=[func_id]) - ) - except ApiException as e: - logger.error( - f"failed to perform similarity request due to {e}" - ) - return - - logger.info( - f"Fetching function similarity result for {func_id}, status: {status.status}" - ) - - if status.status == TaskStatus.FAILED: - logger.error( - "failed to perform similarity request due to matching failure" - ) - return - - if status.status == TaskStatus.COMPLETED: - result: GetMatchesOutputBody = api.get_functions_matches( - function_ids=[func_id] - ) - matches: list[MatchedFunction] = [] - if result.matches: - matches = result.matches[0].matched_functions - - return callback(func_id, vaddr, matches, analysis_id) - - time.sleep(sleep_interval) - elapsed += sleep_interval - - if elapsed >= timeout: - logger.error( - f"failed to perform similarity request due to timing out after {timeout} seconds" - ) - - if stop_event.is_set(): - logger.warning( - "cancelled similarity request due to stop_event being set" - ) - stop_event.clear() diff --git a/reai_toolkit/hooks/popup.py b/reai_toolkit/hooks/popup.py index f4ad79b..7082e0b 100644 --- a/reai_toolkit/hooks/popup.py +++ b/reai_toolkit/hooks/popup.py @@ -8,7 +8,6 @@ DECOMP_ACTION_NAME = "revengai:toggle_ai_decomp" VIEW_ACTION_NAME = "revengai:function_portal_view" MATCHING_ACTION_NAME = "revengai:function_matching_view" -SIMILARITY_ACTION_NAME = "revengai:function_similarity_view" CHAT_ACTION_NAME = "revengai:ask_about_function" _HANDLERS = {} @@ -110,37 +109,6 @@ def _register_function_matching_action(coordinator: Coordinator): return MATCHING_ACTION_NAME -class FunctionSimilarityViewH(kw.action_handler_t): - def __init__(self, coordinator: Coordinator): - super().__init__() - self.coordinator: Coordinator = coordinator - - def activate(self, ctx): - self.coordinator.similarityc.enable_function_tracking() - return 1 - - def update(self, ctx): - return kw.AST_ENABLE_FOR_WIDGET - - -def _register_function_similarity_action(coordinator: Coordinator): - try: - kw.unregister_action(SIMILARITY_ACTION_NAME) - except Exception: - pass - h = FunctionSimilarityViewH(coordinator) # type: ignore - _HANDLERS[SIMILARITY_ACTION_NAME] = h - desc = kw.action_desc_t( - SIMILARITY_ACTION_NAME, - "Find Similar Functions", - h, - None, - "Run RevEng.AI function matching and show similar functions in dockable tab", - ) - kw.register_action(desc) - return SIMILARITY_ACTION_NAME - - class AskAboutFunctionH(kw.action_handler_t): """Opens the Agent Chat panel prefilled with the current function context.""" @@ -179,7 +147,6 @@ def build_hooks(coordinator: Coordinator): decomp_action = _register_decomp_action(coordinator) func_view_action = _register_function_view_action(coordinator) func_matching_action = _register_function_matching_action(coordinator) - func_similarity_action = _register_function_similarity_action(coordinator) chat_action = _register_chat_action(coordinator) def _on_popup(widget, popup_handle) -> None: @@ -201,9 +168,6 @@ def _on_popup(widget, popup_handle) -> None: kw.attach_action_to_popup( widget, popup_handle, func_matching_action, "RevEng.AI/", 0 ) - kw.attach_action_to_popup( - widget, popup_handle, func_similarity_action, "RevEng.AI/", 0 - ) kw.attach_action_to_popup( widget, popup_handle, chat_action, "RevEng.AI/", 0 ) From bd4ad1d621641f76944df9507d6643e1fd9f168a Mon Sep 17 00:00:00 2001 From: Joshua Villalta <93329405+vgoat21@users.noreply.github.com> Date: Mon, 13 Jul 2026 17:04:58 +0100 Subject: [PATCH 2/2] fix: remove tests --- .../unit/matching/test_similarity_service.py | 108 ------------------ 1 file changed, 108 deletions(-) delete mode 100644 tests/unit/matching/test_similarity_service.py diff --git a/tests/unit/matching/test_similarity_service.py b/tests/unit/matching/test_similarity_service.py deleted file mode 100644 index ac3e9a5..0000000 --- a/tests/unit/matching/test_similarity_service.py +++ /dev/null @@ -1,108 +0,0 @@ -import threading -from unittest.mock import MagicMock - -import pytest -from revengai import GetMatchesOutputBody, GetMatchesStatusOutputBody -from revengai.models.function_match import FunctionMatch -from revengai.models.matched_function import MatchedFunction - -from reai_toolkit.app.services.matching import similarity_service as svc_mod -from reai_toolkit.app.services.matching.similarity_service import SimilarityService - -ANALYSIS_ID = 321 -FUNC_ID = 11 -VADDR = 0x401000 - - -@pytest.fixture(autouse=True) -def no_sleep(monkeypatch): - monkeypatch.setattr(svc_mod.time, "sleep", lambda *_: None) - - -@pytest.fixture -def netstore(): - ns = MagicMock() - ns.get_analysis_id.return_value = ANALYSIS_ID - return ns - - -@pytest.fixture -def service(netstore): - return SimilarityService(netstore_service=netstore, sdk_config=MagicMock()) - - -@pytest.fixture -def sdk(mocker): - mocker.patch.object(SimilarityService, "yield_api_client") - functions = mocker.patch.object(svc_mod, "FunctionsCoreApi").return_value - return functions - - -def _matched(function_id: int) -> MatchedFunction: - return MatchedFunction.model_construct(function_id=function_id, similarity=0.9) - - -def _status(status: str) -> GetMatchesStatusOutputBody: - return GetMatchesStatusOutputBody.model_construct( - status=status, step="match", step_index=0, steps_total=1, messages=[] - ) - - -def _matches(matched=None) -> GetMatchesOutputBody: - matches = ( - [FunctionMatch.model_construct(function_id=FUNC_ID, matched_functions=matched)] - if matched is not None - else [] - ) - return GetMatchesOutputBody.model_construct(status="COMPLETED", matches=matches) - - -def _run(service): - cb = MagicMock() - service._perform_function_similarity_request(threading.Event(), FUNC_ID, VADDR, cb) - return cb - - -def test_complete_invokes_callback_with_matches(service, sdk): - sdk.get_functions_matching_status.return_value = _status("COMPLETED") - sdk.get_functions_matches.return_value = _matches( - matched=[_matched(2), _matched(3)] - ) - - cb = _run(service) - - cb.assert_called_once() - func_id, vaddr, matches, analysis_id = cb.call_args[0] - assert (func_id, vaddr, analysis_id) == (FUNC_ID, VADDR, ANALYSIS_ID) - assert [m.function_id for m in matches] == [2, 3] - - -def test_missing_analysis_id_skips(service, sdk, netstore): - netstore.get_analysis_id.return_value = None - - cb = _run(service) - - cb.assert_not_called() - sdk.start_functions_matching.assert_not_called() - - -def test_failed_status_aborts_without_callback(service, sdk): - sdk.get_functions_matching_status.return_value = _status("FAILED") - - cb = _run(service) - - cb.assert_not_called() - sdk.get_functions_matches.assert_not_called() - - -def test_polls_until_status_complete(service, sdk): - sdk.get_functions_matching_status.side_effect = [ - _status("RUNNING"), - _status("COMPLETED"), - ] - sdk.get_functions_matches.return_value = _matches(matched=[_matched(9)]) - - cb = _run(service) - - assert sdk.get_functions_matching_status.call_count == 2 - cb.assert_called_once()