Skip to content

more tools related to project#25

Merged
rabenojha merged 14 commits intomainfrom
dev
Dec 31, 2025
Merged

more tools related to project#25
rabenojha merged 14 commits intomainfrom
dev

Conversation

@iamtekson
Copy link
Copy Markdown
Owner

No description provided.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds project management tools to the GeoAgent QGIS plugin, enabling users to create, save, and load QGIS projects through the AI agent interface.

  • Introduces three new project management functions: create_new_qgis_project, save_qgis_project, and load_qgis_project
  • Implements thread-safe project loading with a new ProjectLoadDispatcher that handles project operations on the main Qt thread
  • Modifies the agent graph to change tool result handling from routing back to the LLM to ending directly after tool execution

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
utils/project_loader.py New module providing thread-safe QGIS project loading with ProjectLoadDispatcher and callback infrastructure
utils/canvas_refresh.py Minor grammar fix in comment ("it will" → "will")
tools/io.py Renames new_qgis_project to create_new_qgis_project and adds save_qgis_project and load_qgis_project functions with validation
tools/init.py Updates exports to include renamed and new project management functions
llm/worker.py Changes result signal type from AIMessage to generic object to support both AIMessage and ToolMessage
geo_agent.py Adds ProjectLoadDispatcher initialization and _project_load_callback implementation with event loop synchronization
agents/graph.py Enhances tool node error handling and changes graph edge from "tools" → "llm" to "tools" → END

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread geo_agent.py
Comment on lines +167 to +188
self._project_load_dispatcher.result_ready.connect(on_result_ready)

# queue the load operation on the main thread
QMetaObject.invokeMethod(
self._project_load_dispatcher,
"doLoadProject",
Qt.QueuedConnection,
Q_ARG(str, path),
)

# wait for the dispatcher to signal completion
loop = QEventLoop()

# max wait 30 seconds timeout
QTimer.singleShot(30000, loop.quit)
loop.exec_()

# disconnect signal to avoid memory leaks
self._project_load_dispatcher.result_ready.disconnect(on_result_ready)

# return the result that was set by the dispatcher
return self._project_load_dispatcher.result
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

There's a potential race condition: if an exception occurs during QMetaObject.invokeMethod or the timeout, the signal will remain connected, causing a memory leak. The disconnect should be in a finally block to ensure it always executes, or use a try-finally pattern around the entire signal handling logic.

Copilot uses AI. Check for mistakes.
Comment thread tools/io.py
Comment on lines +400 to +401
- save_qgis_project('/geoagent/my_project.qgs')
- save_qgis_project('D:/geoagent/project_backup.qgz')
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

There are two trailing spaces at the end of these comment lines, which is inconsistent with the rest of the codebase and can cause issues with some linters and version control systems.

Suggested change
- save_qgis_project('/geoagent/my_project.qgs')
- save_qgis_project('D:/geoagent/project_backup.qgz')
- save_qgis_project('/geoagent/my_project.qgs')
- save_qgis_project('D:/geoagent/project_backup.qgz')

Copilot uses AI. Check for mistakes.
Comment thread geo_agent.py
)

# wait for the dispatcher to signal completion
loop = QEventLoop()
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

The QEventLoop is created after it's used in the on_result_ready function closure. This will cause a NameError when on_result_ready is called because 'loop' is not yet defined at the time the function is created. The loop variable should be created before defining the on_result_ready function.

Copilot uses AI. Check for mistakes.
Comment thread utils/project_loader.py Outdated
Comment on lines +56 to +57
except:
pass
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

Bare except clause silently catches all exceptions including system-exiting exceptions like KeyboardInterrupt and SystemExit. This can mask serious errors and make debugging difficult. Consider catching specific exception types or at least logging the exception before suppressing it.

Copilot uses AI. Check for mistakes.
Comment thread utils/project_loader.py Outdated
Comment on lines +78 to +79
except:
pass
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

Bare except clause silently catches all exceptions including system-exiting exceptions like KeyboardInterrupt and SystemExit. This can mask serious errors and make debugging difficult. Consider catching specific exception types or at least logging the exception before suppressing it.

Copilot uses AI. Check for mistakes.
Comment thread geo_agent.py
loop = QEventLoop()

# max wait 30 seconds timeout
QTimer.singleShot(30000, loop.quit)
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

The magic number 30000 (30 seconds) is used directly without explanation. Consider defining it as a named constant like PROJECT_LOAD_TIMEOUT_MS = 30000 to improve code readability and maintainability.

Copilot uses AI. Check for mistakes.
Comment thread agents/graph.py
graph.set_entry_point("llm")
graph.add_conditional_edges("llm", should_use_tools, {"tools": "tools", END: END})
graph.add_edge("tools", "llm")
graph.add_edge("tools", END)
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

Changing the graph edge from "tools" → "llm" to "tools" → END fundamentally alters the agent's behavior. Previously, tool results would be sent back to the LLM for interpretation and natural language response generation. Now, tool execution results go directly to END, meaning the last message will be a ToolMessage with raw tool output instead of an AIMessage with a natural language response. This breaks the conversational flow as users will see raw tool outputs like "Success: Layer added..." instead of the AI interpreting and presenting results naturally.

Suggested change
graph.add_edge("tools", END)
graph.add_edge("tools", "llm")

Copilot uses AI. Check for mistakes.
Comment thread geo_agent.py
Comment on lines +180 to +182
# max wait 30 seconds timeout
QTimer.singleShot(30000, loop.quit)
loop.exec_()
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

The timeout timer is started but there's no mechanism to stop it if the result_ready signal fires first. This means the timer will continue to run and could potentially call loop.quit() even after the loop has already quit from the signal, which could cause issues. Consider storing the timer and calling stop() on it after the loop exits.

Copilot uses AI. Check for mistakes.
Comment thread agents/graph.py
Graph builder for GeoAgent: routes between general conversational mode and geoprocessing workflows.
"""
from typing import List, Any
from langchain_core.messages import SystemMessage, HumanMessage
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

Import of 'SystemMessage' is not used.
Import of 'HumanMessage' is not used.

Suggested change
from langchain_core.messages import SystemMessage, HumanMessage

Copilot uses AI. Check for mistakes.
@iamtekson
Copy link
Copy Markdown
Owner Author

@rabenojha can you work on Copilot suggestions? Let's try to implement the good tips and ignore unnecessary suggestions.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 20 out of 20 changed files in this pull request and generated 26 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tools/io.py
_logger.info(f"Successfully loaded project from '{path}'")
return f"Success: Project loaded from '{path}'"
else:
_logger.error(f"Unknown error loading project from '{path}'")
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

The function returns None at the end when it should return an error message. After the except block catches an exception and logs it at line 527, control falls through without a return statement. This means the function will return None instead of an error message string, which is inconsistent with the function's return type annotation and other error paths.

Suggested change
_logger.error(f"Unknown error loading project from '{path}'")
_logger.error(f"Unknown error loading project from '{path}'")
return f"Error: Unknown error loading project from '{path}'"

Copilot uses AI. Check for mistakes.
Comment thread utils/layer_operations.py Outdated
self.result["error"] = str(e)
try:
QgsMessageLog.logMessage(str(e), "GeoAgent", level=Qgis.Warning)
except:
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

Bare except clause will catch all exceptions including system exits and keyboard interrupts, which can make the code difficult to debug and interrupt. Consider catching specific exception types or at least Exception instead of using a bare except.

Suggested change
except:
except Exception:

Copilot uses AI. Check for mistakes.
Comment thread logger/__init__.py
Comment on lines +6 to +15
get_processing_logger,
set_processing_ui_log_handler,
)

__all__ = [
"UILogHandler",
"get_logger",
"UILogSignal",
"get_processing_logger",
"set_processing_ui_log_handler",
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

Using inconsistent indentation - tabs instead of spaces. The rest of the codebase uses spaces for indentation (as seen in other files), but this file uses tabs on lines 6 and 7. Python PEP 8 recommends using 4 spaces per indentation level, and mixing tabs and spaces can cause issues.

Suggested change
get_processing_logger,
set_processing_ui_log_handler,
)
__all__ = [
"UILogHandler",
"get_logger",
"UILogSignal",
"get_processing_logger",
"set_processing_ui_log_handler",
get_processing_logger,
set_processing_ui_log_handler,
)
__all__ = [
"UILogHandler",
"get_logger",
"UILogSignal",
"get_processing_logger",
"set_processing_ui_log_handler",

Copilot uses AI. Check for mistakes.
Comment thread utils/canvas_refresh.py
Comment on lines +78 to +80



Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

Grammar fix looks good but there are two trailing blank lines (78-80) which is inconsistent with the project style. Most Python files should end with a single blank line.

Suggested change

Copilot uses AI. Check for mistakes.
Comment thread tools/io.py Outdated
Comment on lines 387 to 388
- new_qgis_project('/geoagent/my_project.qgs')
- new_qgis_project('/geoagent/test_project.qgz', 'QGIS Test Project')
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

The docstring examples still reference the old function name "new_qgis_project" instead of the new function name "create_new_qgis_project". This will confuse users reading the documentation.

Suggested change
- new_qgis_project('/geoagent/my_project.qgs')
- new_qgis_project('/geoagent/test_project.qgz', 'QGIS Test Project')
- create_new_qgis_project('/geoagent/my_project.qgs')
- create_new_qgis_project('/geoagent/test_project.qgz', 'QGIS Test Project')

Copilot uses AI. Check for mistakes.
Comment thread utils/layer_operations.py Outdated
self.result["error"] = str(e)
try:
QgsMessageLog.logMessage(str(e), "GeoAgent", level=Qgis.Warning)
except:
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

'except' clause does nothing but pass and there is no explanatory comment.

Suggested change
except:
except Exception:
# Ignore logging failures to avoid masking the original exception.

Copilot uses AI. Check for mistakes.
Comment thread logger/logger.py
self.text_browser.verticalScrollBar().setValue(
self.text_browser.verticalScrollBar().maximum()
)
except Exception:
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

'except' clause does nothing but pass and there is no explanatory comment.

Copilot uses AI. Check for mistakes.
Comment thread logger/logger.py

# Update line count
self.line_count = self.text_browser.document().lineCount()
except Exception:
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

'except' clause does nothing but pass and there is no explanatory comment.

Copilot uses AI. Check for mistakes.
Comment thread utils/project_loader.py Outdated
# trigger UI updates
try:
project.readProject.emit()
except:
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

'except' clause does nothing but pass and there is no explanatory comment.

Copilot uses AI. Check for mistakes.
Comment thread utils/project_loader.py Outdated
self.result["error"] = str(e)
try:
QgsMessageLog.logMessage(str(e), "GeoAgent", level=Qgis.Warning)
except:
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

'except' clause does nothing but pass and there is no explanatory comment.

Copilot uses AI. Check for mistakes.
@rabenojha rabenojha merged commit c6e0571 into main Dec 31, 2025
1 check passed
@iamtekson iamtekson deleted the dev branch December 31, 2025 01:10
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.

4 participants