-
Notifications
You must be signed in to change notification settings - Fork 0
feat(script-tools): add GhJSON-based script tools, merge workflows, and message-boxes to canvas linking #354
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…eflect unstable development status
- Added programming language keywords (Python, C#, VB, IronPython) - Added code-related terms (programming, code, codex) to improve search visibility
… into feature/1.2.0-script-tools
Updated manifest description to be more concise and improved formatting with separators. Changed project URL from GitHub repository to smarthopper.xyz website.
…mponents Changed AI-selecting stateful components to use AISelectingComponentAttributes instead of SelectingComponentAttributes, rendering both the Select button and provider badges together. The button appears above the provider strip with proper layout spacing and includes hover/click states with a 5-second auto-hide timer for selected object highlights.
…lify scripting components
…s when not specified in GhJSON Changed deserialization logic to default the UsingStandardOutputParam property to true when ShowStandardOutput is not present in the GhJSON ComponentState, ensuring script components show the "out" parameter by default rather than hiding it.
…g in script generator Updated AIScriptGeneratorComponent to output summary for both generate and edit modes, consolidating summary/changesSummary handling. Changed all error messages to use "Information" output parameter for consistency. Updated tool descriptions to clarify that summary should include design decisions, and made summary a required field in script_generate tool schema.
…or and Review components Updated AIScriptGeneratorComponent and AIScriptReviewComponent to support processing multiple inputs in parallel: - Changed all outputs from item/list to tree access with one branch per input - In create mode: processes each prompt as a separate branch - In edit mode: matches prompts/questions to selected components using DataTreeProcessor.NormalizeBranchLengths for first-first, second-second matching
Added Edit Mode functionality to GhPutComponents component and gh_put AI tool: - Added "Edit Mode" boolean input parameter to GhPutComponents that defaults to false - When enabled and GhJSON contains valid instanceGuids matching canvas components, users are prompted via StyledMessageDialog to choose between replacing existing components or creating new ones - The gh_put AI tool now accepts optional editMode parameter in its schema
Added DialogCanvasLink utility that draws visual connection lines from dialogs to linked Grasshopper components: - Created DialogCanvasLink class that manages visual connections between dialogs and canvas components using bezier curves with anchor dots, similar to script editor anchors - StyledMessageDialog methods now accept optional linkedInstanceGuid and linkLineColor parameters to enable canvas linking - Registered canvas link callback in
…sual jitter Added bounds caching in SelectingComponentAttributes and AISelectingComponentAttributes to fix visual instability when highlighting selected objects: - Introduced cachedSelectedBounds dictionary that stores component bounds when hover starts - Bounds are computed once via CacheSelectedBounds() method and reused during entire hover session - Cache is cleared when hover ends to ensure fresh positions on next hover
…for dialogs Enhanced component replacement workflow and dialog positioning: - gh_put tool now prompts user individually for each component replacement instead of batch confirmation, allowing selective replacement - Added CenterViewOnComponent method to CanvasAccess that pans canvas to position components at specified horizontal location (0=left, 0.5=center, 1=right), skipping pan if component already in central 2/3 of viewport
…t with document merging Enhanced gh_put tool to preserve connections when replacing components: - Capture components with depth=1 connections before replacement using ConnectionGraphUtils.ExpandByDepth - Merge captured document into incoming document via new GhJsonMerger utility that handles component/connection/group merging with ID remapping and deduplication - Disable GH_Document during replacement to prevent solution recalculation
…rge component Added comprehensive GhJSON document merging functionality: - Created GhJsonMerger utility to merge two GrasshopperDocument instances with target document taking priority on component GUID conflicts and automatic ID remapping for connections and groups - Introduced gh_merge AI tool that merges two GhJSON strings and returns merged GhJSON with detailed merge statistics (components/connections/groups added and deduplicated)
…omponent replacement Fixed two critical issues in gh_put tool's replacement mode: - Removed NewSolution call that caused infinite loop when GhPutComponents blocked with GetAwaiter().GetResult(), which pumps Windows messages and allows re-entrant solution execution - Added connection cleanup before component removal by calling RemoveAllSources on inputs and RemoveSource on all output recipients,
…ExecuteTool Enhanced AIToolManager.ExecuteTool with caller assembly signature verification to prevent unauthorized tool execution: - Added VerifyCallerAssembly method that validates both Authenticode certificate thumbprint and strong-name public key token match between caller and host assembly - Cached host assembly's certificate thumbprint and public key token in lazy-initialized static fields for performance
…ure verification Hardened AIToolManager.VerifyCallerAssembly to require proper signing in all scenarios: - Removed development mode bypasses that allowed unsigned assemblies when host was unsigned - Now throws SecurityException if host assembly lacks either Authenticode signature or strong-name signature - Updated Sign-Authenticode.ps1 to sign all SmartHopper*.dll assemblies instead of only specific provider assemblies
Changed Sign-Authenticode.ps1 to sign all SmartHopper*.dll files instead of only provider and infrastructure assemblies. Simplified filtering logic from explicit name matching to wildcard pattern matching.
…ExecuteTool Enhanced AIToolManager.ExecuteTool with caller assembly signature verification to prevent unauthorized tool execution: - Added VerifyCallerAssembly method that validates both Authenticode certificate thumbprint and strong-name public key token match between caller and host assembly - Cached host assembly's certificate thumbprint and public key token in lazy-initialized static fields for performance
…tects-toolkit/SmartHopper into feature/1.2.0-script-tools
…nentBase and improve connection capture logic Refactored GhPutComponents to use StatefulAsyncComponentBase for proper async execution and state management: - Changed GhPutComponents from GH_Component to StatefulAsyncComponentBase with GhPutWorker to prevent re-entrancy issues - Set RunOnlyOnInputChanges to false to allow re-placing same JSON multiple times - Improved deprecated badge appearance
…in selecting components Enhanced SelectingComponentBase and CombinedSelectingComponentAttributes to draw a visual connector line from the combined selection center to the Select button during hover: - Changed highlight color from DodgerBlue to DialogCanvasLink.DefaultLineColor for consistency - Calculate union of all selected object bounds and draw connector line from center to button center - Added anchor dots at both ends of connector
…fix OpenAI structured output compliance
…eason/model in metrics Added structured output support for JsonOutput requests using `response_format: json_schema` and `structured_outputs` flag. Now populates `finish_reason` and `model` fields in AIMetrics for chat completions.
…rasshopper category or subcategory Added category filtering capability to gh_get tool and GhGetComponents: - Added "Category Filter" input parameter (index 1) with include/exclude syntax - Implemented PassesCategoryFilters method in ComponentRetriever to match against both Category and SubCategory fields - Updated gh_get tool schema to document categoryFilter parameter
…ting" to "NotTested" for all script parameter modification tools
… and clarify required workflows
…TurnId propagation - Fixed chat UI to display total token consumption per turn (from user message to next user message) instead of just the last message's metrics - Added `GetTurnMetrics(turnId)` method to `ConversationSession` to aggregate metrics from all interactions in a turn (assistant messages, tool calls, and tool results) - Fixed tool results not inheriting TurnId from their corresponding tool calls in `ToolManager.ExecuteTool`
… to reduce token consumption Added a convenience wrapper tool that combines script_edit and gh_put in a single call: - Created script_edit_and_replace_on_canvas tool that internally executes script_edit followed by gh_put with editMode=true - Changed script_edit category from "Scripting" to "Hidden" since the wrapper is now the preferred interface - Updated CanvasButton system prompt to document the new workflow using the wrapper tool
…cript_generate and script_edit Enhanced script_generate and script_edit tools to support all parameter modifiers for both inputs and outputs: - Added support for dataMapping (Flatten/Graft), reverse, simplify, invert, isPrincipal, required, and expression modifiers for input parameters - Added support for dataMapping, reverse, simplify, and invert modifiers for output parameters - Updated tool schemas to document all available
…Rhino focus and stays on top - Set WebChatDialog as an owned window of Rhino's main Eto window and hide it from the taskbar - Dialog now follows Rhino/Grasshopper focus and stays on top of Rhino while the application is active - Fixed issue where confirmation dialogs (e.g., from gh_put in edit mode) would leave the chat window hidden behind other windows after closing
…se instanceGuid and make parameter schemas more explicit - Changed script_edit_and_replace_on_canvas wrapper to accept instanceGuid instead of ghjson, automatically calling gh_get_by_guid internally before script_edit and gh_put - Made all optional parameter modifier fields required in JSON schemas for script_generate and script_edit to force explicit specification - Added guidance text to parameter descriptions
…nnecessary code blocks and external testing patterns - Added guidance that all scripting happens inside Grasshopper script components, not standalone environments - Instructed to avoid proposing unit tests or external test projects since validation happens directly in Grasshopper - Added instruction to tell users to double-click script components instead of pasting full scripts in chat - Discouraged copying full scripts into the conversation
|
🏷️ This PR has been automatically assigned to milestone 1.2.0-alpha based on the version in |
There was a problem hiding this 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 introduces a comprehensive script tools feature set (version 1.2.0) that enables AI-powered script component generation and editing through GhJSON workflows, along with improved canvas interaction and dialog UX enhancements.
- GhJSON-based script tools: Introduces
script_generateandscript_editAI tools that operate on GhJSON for creating and modifying script components, with support for comprehensive parameter modifiers - Component replacement workflow: Extends
gh_putwith edit mode to replace existing components in-place with user confirmation and connection preservation - Dialog-canvas linking: Adds visual bezier lines connecting dialogs to related canvas components for improved spatial awareness
- Infrastructure improvements: Enhances metrics tracking (per-turn aggregation), adds structured output for OpenRouter, registers Claude Opus 4.5, and improves selection component UI
Reviewed changes
Copilot reviewed 74 out of 86 changed files in this pull request and generated 32 comments.
Show a summary per file
| File | Description |
|---|---|
| yak-package/manifest.yml | Updates package description, URL, and keywords for script tools release |
| tools/Update-InternalsVisibleTo.ps1 | New PowerShell script to update InternalsVisibleTo with public key from signing.snk |
| src/SmartHopper.Infrastructure/SmartHopper.Infrastructure.csproj | Adds InternalsVisibleTo configuration with public key for controlled access |
| src/SmartHopper.Infrastructure/Dialogs/StyledMessageDialog.cs | Changes visibility to internal, adds canvas linking support, implements dynamic sizing |
| src/SmartHopper.Infrastructure/AITools/ToolManager.cs | Propagates TurnId from tool calls to tool results |
| src/SmartHopper.Infrastructure/AICall/Sessions/ConversationSessionHelpers.cs | Adds helpers for non-streaming follow-ups and tool call history updates |
| src/SmartHopper.Infrastructure/AICall/Sessions/ConversationSession.cs | Implements GetTurnMetrics and UpdateToolCallInHistory for streaming accumulation |
| src/SmartHopper.Infrastructure/AICall/Core/Returns/AIReturn.cs | Adds conditional metrics validation based on SkipMetricsValidation flag |
| src/SmartHopper.Infrastructure/AICall/Core/Requests/AIRequestBase.cs | Adds SkipMetricsValidation property for local tools |
| src/SmartHopper.Providers.OpenRouter/OpenRouterProvider.cs | Implements structured output via response_format and improves metrics extraction |
| src/SmartHopper.Providers.Anthropic/AnthropicProviderModels.cs | Registers Claude Opus 4.5 model and updates model ranks |
| src/SmartHopper.Core/UI/DialogCanvasLink.cs | New utility for drawing visual links between dialogs and canvas components |
| src/SmartHopper.Core/UI/Chat/WebChatObserver.cs | Uses GetTurnMetrics for per-turn metric aggregation in chat UI |
| src/SmartHopper.Core/UI/Chat/WebChatDialog.cs | Sets dialog as owned window with no taskbar entry |
| src/SmartHopper.Core/UI/CanvasButton.cs | Updates default system prompt with scripting workflow guidance |
| src/SmartHopper.Core/ComponentBase/* | Enhances selection components with visual overlays and shared rendering logic |
| src/SmartHopper.Core.Grasshopper/Utils/* | Adds CenterViewOnComponent and PassesCategoryFilters utilities |
| src/SmartHopper.Core.Grasshopper/Serialization/GhJson/* | Adds GhJsonHelpers, GhJsonMerger, and extends ScriptComponentFactory |
| src/SmartHopper.Core.Grasshopper/Serialization/Canvas/ComponentPlacer.cs | Adds useExactPositions mode for replacement scenarios |
| src/SmartHopper.Core.Grasshopper/AITools/script_generate.cs | New tool for generating script components as GhJSON |
| src/SmartHopper.Core.Grasshopper/AITools/script_edit.cs | New tool for editing script components via GhJSON with wrapper for canvas replacement |
| src/SmartHopper.Core.Grasshopper/AITools/gh_put.cs | Implements edit mode with component replacement and connection preservation |
| src/SmartHopper.Core.Grasshopper/AITools/gh_merge.cs | New tool for merging two GhJSON documents |
| src/SmartHopper.Core.Grasshopper/AITools/gh_get.cs | Adds categoryFilter support for filtering by Grasshopper category |
| src/SmartHopper.Core.Grasshopper/AITools/* | Adds SkipMetricsValidation to local tools |
| src/SmartHopper.Components/Script/* | New AIScriptGeneratorComponent and AIScriptReviewComponent |
| src/SmartHopper.Components/SmartHopperAssemblyPriority.cs | Registers canvas centering and dialog link callbacks |
Files not reviewed (1)
- src/SmartHopper.Components/Properties/Resources.Designer.cs: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| foreach (var msg in aiResult.Messages) | ||
| { | ||
| if (!string.IsNullOrWhiteSpace(msg?.Message)) | ||
| { | ||
| parts.Add($"{msg.Severity}: {msg.Message}"); | ||
| } | ||
| } |
Copilot
AI
Dec 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This foreach loop implicitly filters its target sequence - consider filtering the sequence explicitly using '.Where(...)'.
| foreach (var msg in (JArray)toolResult["messages"]) | ||
| { | ||
| if (msg["severity"]?.ToString() == "Error") | ||
| { | ||
| var text = msg["message"]?.ToString(); | ||
| if (!string.IsNullOrWhiteSpace(text)) | ||
| { | ||
| this.AddRuntimeMessage(GH_RuntimeMessageLevel.Error, text); | ||
| } | ||
| } | ||
| } |
Copilot
AI
Dec 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This foreach loop implicitly filters its target sequence - consider filtering the sequence explicitly using '.Where(...)'.
| foreach (var msg in (JArray)toolResult["messages"]) | ||
| { | ||
| if (msg["severity"]?.ToString() == "Error") | ||
| { | ||
| var text = msg["message"]?.ToString(); | ||
| if (!string.IsNullOrWhiteSpace(text)) | ||
| { | ||
| this.AddRuntimeMessage(GH_RuntimeMessageLevel.Error, text); | ||
| } | ||
| } | ||
| } |
Copilot
AI
Dec 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This foreach loop implicitly filters its target sequence - consider filtering the sequence explicitly using '.Where(...)'.
| foreach (var obj in selectingComponent.SelectedObjects.OfType<IGH_DocumentObject>()) | ||
| { | ||
| if (obj.Attributes != null) | ||
| { | ||
| result[obj.InstanceGuid] = obj.Attributes.Bounds; | ||
| } | ||
| } |
Copilot
AI
Dec 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This foreach loop implicitly filters its target sequence - consider filtering the sequence explicitly using '.Where(...)'.
| foreach (var typeStr in typeFilterArray) | ||
| { | ||
| if (Enum.TryParse<ObjectType>(typeStr.ToString(), true, out var objType)) | ||
| { | ||
| typeFilter.Add(objType); | ||
| } | ||
| } |
Copilot
AI
Dec 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This foreach loop implicitly filters its target sequence - consider filtering the sequence explicitly using '.Where(...)'.
| if (obj is IGH_Component comp) | ||
| { | ||
| if (comp.Params.Input.Contains(param) || comp.Params.Output.Contains(param)) | ||
| { | ||
| owner = comp; | ||
| break; | ||
| } | ||
| } |
Copilot
AI
Dec 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These 'if' statements can be combined.
| internal class StyledMessageDialog : Dialog | ||
| { | ||
| private bool _result; | ||
| private Guid _linkedInstanceGuid = Guid.Empty; |
Copilot
AI
Dec 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Field '_linkedInstanceGuid' can be 'readonly'.
| { | ||
| private bool _result; | ||
| private Guid _linkedInstanceGuid = Guid.Empty; | ||
| private System.Drawing.Color? _linkLineColor; |
Copilot
AI
Dec 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Field '_linkLineColor' can be 'readonly'.
| if (!string.IsNullOrWhiteSpace(turnId)) | ||
| { | ||
| aggregated.Metrics = this._dialog._currentSession?.GetTurnMetrics(turnId) ?? finalAssistant.Metrics; | ||
| } | ||
| else | ||
| { | ||
| aggregated.Metrics = finalAssistant.Metrics; | ||
| } |
Copilot
AI
Dec 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both branches of this 'if' statement write to the same variable - consider using '?' to express intent better.
Sign-Authenticode.ps1
Outdated
| $items = Get-ChildItem -Path $Sign -Recurse -Filter "*.dll" | | ||
| Where-Object { $_.Name -like "SmartHopper.Providers.*.dll" -or $_.Name -eq "SmartHopper.Infrastructure.dll" } | ||
| Write-Host "Signing all SmartHopper assemblies under directory: $Sign" | ||
| $items = Get-ChildItem -Path $Sign -Recurse -Filter "SmartHopper*.dll" |
Copilot
AI
Dec 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The broadened signing glob in $items = Get-ChildItem -Recurse -Filter "SmartHopper*.dll" risks code-signing unintended or attacker-planted assemblies. If an untrusted DLL matching SmartHopper*.dll is present anywhere under $Sign, this script will sign it, lending trust to malicious code and enabling supply-chain compromise. Restrict signing to an explicit allowlist of known assemblies or a manifest from the build system (e.g., enumerate exact project output names/paths), and validate binaries before signing (e.g., ensure they originate from expected build directories and match expected file names/hashes).
- Replaced foreach loops with LINQ expressions (Where, Select, ToHashSet, etc.) across multiple files for more concise code - Added explicit assembly allowlist to Sign-Authenticode.ps1 to prevent signing unintended/malicious assemblies - Added validation to reject non-SmartHopper assemblies when signing explicit DLLs - Changed floating point comparison in script_edit.cs to use tolerance instead of direct equality check
feat(script-tools): add GhJSON-based script tools, merge workflows, and dialog-canvas linking
Description
This PR prepares the 1.2.0 “script tools” feature set, focusing on GhJSON‑based scripting workflows, GhJSON merge utilities, and improved in-canvas UX:
GhJSON-based script tools
script_generateandscript_editAI tools that operate purely on GhJSON for Grasshopper script components.GHJsonAnalyzer.Validateand useScriptComponentFactoryfor component construction.script_edit_and_replace_on_canvaswrapper tool that chainsscript_editandgh_putin one call, reducing token usage and simplifying AI-driven script editing.dataMapping(Flatten/Graft),reverse,simplify,invert,isPrincipal,required,expressiondataMapping,reverse,simplify,invertGhJSON helpers and merge workflows
GhJsonHelpersutility to apply pivots and restoreInstanceGuids on deserialized components.GhJsonMergerto merge two GhJSONGrasshopperDocumentinstances with:gh_mergeAI tool andGhMergeComponentsGrasshopper component for merging GhJSON documents, exposing merged GhJSON and basic merge statistics.Component replacement mode and canvas-linked messabe boxes
GhPutComponentswith an “Edit Mode” input to support component replacement based on existingInstanceGuids.gh_putto accept aneditModeparameter, preserving originalInstanceGuidand exact canvas positions when replacing components, with undo support.DialogCanvasLinkutility and extendStyledMessageDialogAPIs with optionallinkedInstanceGuidandlinkLineColorso dialogs can visually link to Grasshopper components via a bezier line and anchor dot on the canvas.gh_getimprovementscategoryFiltersupport togh_get, extending category-based filtering from components to all document objects.Component and UI updates
CanvasButtonto guide users toward in‑viewport scripting workflows and avoid unnecessary external code blocks or testing patterns.ghmerge, updatedghget/ghput) for better visual consistency.Provider and metrics improvements
response_format: json_schema/structured_outputsand improved metrics (finish_reason,model).ConversationSession.GetTurnMetrics, and ensure tool results inherit the correctTurnId.Stability and UX fixes
gh_putinfinite loop and “object expired during solution” errors when using replacement mode by removing problematic document enable/disable logic and relying onIsolateObject()during cleanup.WebChatDialogis an owned Rhino tool window (no taskbar entry, stays with Rhino/Grasshopper focus) and plays nicely with confirmation dialogs.script_generateandscript_editschemas to avoid OpenAI structured-output crashes.Breaking Changes
Get ComponentsorPlace Componentscomponents might have to replace them with the new ones.Testing Done
AIScriptGeneratorComponentusingscript_generateand validate GhJSON round‑trip withgh_put.AIScriptGeneratorComponent+script_edit_and_replace_on_canvasand confirm the component is replaced in place with preserved position and stable behavior.GhMergeComponents(andgh_merge) to merge two GhJSON documents and verify:- Components, connections, and groups are merged as expected
- Merge statistics match the visual result
GhPutComponentsEdit Mode to replace existing components and confirm:- Prompt behavior via
StyledMessageDialogis correct- Undo restores the previous state
GhPutComponentsconfirmations) and visually verify dialog-canvas link lines and hover behavior.gh_getwithcategoryFilterreturns only the expected objects.SmartHopper.Core.Grasshopper.Tests) and ensure they pass.Checklist