Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
cdbfb4a
fix(model): bypass capability validation for unregistered models
marc-romu Oct 12, 2025
2e8e9ed
refactor(validation): issue comprehensive messages when model is not …
marc-romu Oct 12, 2025
a0a017e
refactor(tests): test that unregistered model bypass validation
marc-romu Oct 12, 2025
cfdb483
refactor(tests): add extra tests for capability validation
marc-romu Oct 12, 2025
eb706c0
fix(model): bypass capability validation for unregistered models (#330)
marc-romu Oct 12, 2025
f50265a
docs: update version badge for dev
actions-user Oct 12, 2025
f68c557
docs: update version badge for dev to 1.0.1-dev.251012 (#331)
marc-romu Oct 12, 2025
138b6d6
refactor(badges): show invalid model badge when provider has no capab…
marc-romu Oct 12, 2025
21d5c11
refactor(badges): show invalid model badge when provider has no capab…
marc-romu Oct 12, 2025
af421ac
fix(component): fix model badge display (#336)
marc-romu Oct 12, 2025
b480617
refactor(openai): make reasoning effort default to low
marc-romu Oct 12, 2025
a7344ca
refactor(tool-manager): properly surface runtime messages
marc-romu Oct 12, 2025
236cac3
refactor(ai-calls): centralized error messages
marc-romu Oct 12, 2025
ab2be85
refactor(finish-reason-policy): clearer message on length finish reason
marc-romu Oct 12, 2025
5074342
performance(ai-stateful-async): prevent updating badges on post-solve
marc-romu Oct 12, 2025
ee4c201
refactor(list-generate): batch retry and optimization
marc-romu Oct 12, 2025
0335eaf
refactor(conversation-session): accuratelly aggregate metrics in conv…
marc-romu Oct 12, 2025
9a163cd
refactor(conversation-session): add interaction completion time
marc-romu Oct 12, 2025
fbdde63
refactor(conversation-session): unified error handling
marc-romu Oct 12, 2025
c567924
refactor: code style
marc-romu Oct 13, 2025
02f0d11
docs(changelog): mention fixed issue
marc-romu Oct 13, 2025
f13edfb
fix(core): improve error handling in ConversationSession and metrics …
marc-romu Oct 13, 2025
2af4450
chore: update development version date to 1.0.1-dev.251013
actions-user Oct 13, 2025
43b126d
refactor(ai-tools): improved descriptions and specific tool wrappers
marc-romu Oct 13, 2025
123fa7b
refactor(ai-tools): fix compilation errors
marc-romu Oct 13, 2025
d2c3566
chore: update development version date to 1.0.1-dev.251013 (#338)
marc-romu Oct 13, 2025
cff1cfc
fix(gh-get): specialized ai tools (#339)
marc-romu Oct 13, 2025
be43dfc
refactor(list-filter): improved prompt
marc-romu Oct 13, 2025
02171b5
refactor(list-filter): do not automatically sort and deduplicate indices
marc-romu Oct 13, 2025
e7a9bf3
refactor(list-filter): do not automatically sort and deduplicate indices
marc-romu Oct 13, 2025
73acfb5
fix(list-filter): prevent automatic sorting and deduplication of resu…
marc-romu Oct 13, 2025
efe93aa
refactor(models): added more predefined models
marc-romu Oct 13, 2025
cd798e5
docs
marc-romu Oct 13, 2025
9184449
ci: code style
marc-romu Oct 13, 2025
60d1edc
refactor(models): added more predefined models (#341)
marc-romu Oct 13, 2025
bb1e05d
chore: prepare release 1.0.1-alpha with version update and code style…
actions-user Oct 13, 2025
2f0d3ea
docs(changelog): add missing issue fixes
marc-romu Oct 13, 2025
3ec2d1c
chore: prepare release 1.0.1-alpha with version update and code style…
marc-romu Oct 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [1.0.1-alpha] - 2025-10-13

### Changed

- Model capability validation now bypasses checks for unregistered models, allowing users to use any model name even if not explicitly listed in the provider's model registry.
- Centralized error handling in AIReturn and tool calls.
- Accurately aggregate metrics in Conversation Session. Cases with multiple tool calls, multiple interactions, etc. Calculate completion time per interaction.
- Improved AI Tool descriptions with better guided instructions. Also added specialized wrappers for targeted tool calls (gh_get_selected, gh_get_errors, gh_get_locked, gh_get_hidden, gh_get_visible, gh_get_by_guid, gh_lock_selected, gh_unlock_selected, gh_hide_preview_selected, gh_show_preview_selected, gh_group_selected, gh_tidy_up_selected).
- Enhanced `list_filter` tool prompts to explicitly distinguish between indices (positions/keys) and values (item content), and expanded capabilities to support filtering, sorting, reordering, selecting, and other list manipulation operations based on natural language criteria.
- Added more predefined models in the provider's database.

### Fixed

- Fixed model badge display: show "invalid model" badge when provider has no capable model instead of "model replaced" ([#332](https://github.com/architects-toolkit/SmartHopper/issues/332)) ([#329](https://github.com/architects-toolkit/SmartHopper/issues/329)).
- Fixed provider errors (e.g., HTTP 400, token limit exceeded) not surfacing to WebChat UI: `ConversationSession` now surfaces `AIInteractionError` from error AIReturn bodies to observers before calling `OnError`, ensuring full error messages are displayed in the chat interface ([#334](https://github.com/architects-toolkit/SmartHopper/issues/334)).
- Fixed `list_filter` tool automatically sorting and deduplicating indices, which prevented reordering and expansion operations from working correctly. Now preserves both order and duplicates as returned by the AI ([#335](https://github.com/architects-toolkit/SmartHopper/issues/335)).

## [1.0.0-alpha] - 2025-10-11

### Added
Expand Down
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# SmartHopper - AI-Powered Tools and Assistant for Grasshopper3D

[![Version](https://img.shields.io/badge/version-1.0.0--alpha-orange?style=for-the-badge)](https://github.com/architects-toolkit/SmartHopper/releases)
[![Version](https://img.shields.io/badge/version-1.0.1--alpha-orange?style=for-the-badge)](https://github.com/architects-toolkit/SmartHopper/releases)
[![Status](https://img.shields.io/badge/status-Alpha-orange?style=for-the-badge)](https://github.com/architects-toolkit/SmartHopper/releases)
[![.NET CI](https://img.shields.io/github/actions/workflow/status/architects-toolkit/SmartHopper/.github/workflows/ci-dotnet-tests.yml?label=tests&logo=dotnet&style=for-the-badge)](https://github.com/architects-toolkit/SmartHopper/actions/workflows/ci-dotnet-tests.yml)
[![Ready to use](https://img.shields.io/badge/ready_to_use-YES-brightgreen?style=for-the-badge)](https://smarthopper.xyz/#installation)
Expand Down Expand Up @@ -60,6 +60,12 @@ Do things that were impossible before.

[View the video on Vimeo](https://vimeo.com/1126454744)

Choose a default provider, or specify a provider for each component.

[![Select AI provider ▶](./img/video-select-provider.jpg)](https://vimeo.com/1126547055 "Select AI provider ▶ — click to watch on Vimeo")

[View the video on Vimeo](https://vimeo.com/1126547055)

More examples and recipes coming soon on the website and docs.

Developer details (AI tools, providers, data types, status) can be found in [DEV.md](DEV.md).
Expand Down
2 changes: 1 addition & 1 deletion Solution.props
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<Project>
<PropertyGroup>
<SolutionVersion>1.0.0-alpha</SolutionVersion>
<SolutionVersion>1.0.1-alpha</SolutionVersion>
</PropertyGroup>
</Project>
19 changes: 14 additions & 5 deletions docs/Providers/AICall/body-metrics-status.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,25 @@ Covers `AIBody`, `AIMetrics`, `AICallStatus`, and `AIReturn`.
## AIReturn and IAIReturn

- Files: `AIReturn.cs`, `IAIReturn.cs`
- `AIReturn` carries `Body`, `Request`, aggregated `Metrics`, `Status`, `ErrorMessage`, `Success`
- Validity: checks `Request.IsValid()`, `Metrics.IsValid()`, and that either `Body` or `ErrorMessage` is present
- `AIReturn` carries `Body`, `Request`, aggregated `Metrics`, `Status`, `Messages`, and computed `Success`
- `Success` is computed: returns `true` when no error messages exist in `Messages` collection
- `Messages` aggregates structured runtime messages from:
- Private messages added via `AddRuntimeMessage()` or `Create*Error()` methods
- `Request.Messages` (validation/capability notes)
- `Body.Messages` (interaction and body validation messages)
- Deduplicates and sorts by severity (Error > Warning > Info)
- Validity: checks `Request.IsValid()`, `Metrics.IsValid()`, and that either `Body` or `Messages` is present
- Builders:
- `CreateSuccess(AIBody body, IAIRequest? request = null)`
- `CreateSuccess(List<IAIInteraction> interactions, IAIRequest? request = null, AIMetrics? metrics = null)`
- `CreateSuccess(JObject raw, IAIRequest? request = null)` stores raw provider JSON; decoding to interactions is handled by response policies
- `CreateError(string message, IAIRequest? request = null)`
- `CreateProviderError(string rawMessage, IAIRequest? request = null)` and `CreateNetworkError(...)` add standardized structured messages while preserving raw error text
- `CreateError(string message, IAIRequest? request = null)` adds structured error message with Return origin
- `CreateProviderError(string rawMessage, IAIRequest? request = null)` adds structured error with Provider origin
- `CreateNetworkError(string rawMessage, IAIRequest? request = null)` adds structured error with Network origin
- `CreateToolError(string rawMessage, IAIRequest? request = null)` adds structured error with Tool origin
- `SetBody(...)` overloads update `Body` directly
- `AIReturnExtensions.ToJObject(...)` maps selected fields from `AIReturn`, `Request`, `Metrics` via reflection; customizable mapping
- `AddRuntimeMessage(severity, origin, text)` adds structured messages without affecting Success flag directly
- `AIReturnExtensions.ToJObject(...)` maps selected fields from `AIReturn`, `Request`, `Metrics` via reflection; default mapping includes `messages` instead of legacy `error`

Policy notes:

Expand Down
17 changes: 9 additions & 8 deletions docs/Providers/AICall/messages.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ This document explains the centralized propagation and aggregation of structured
- Interaction messages:
- `AIInteractionToolResult.Messages` for tool outputs.
- `AIInteractionImage.Messages` for image outputs.
- Error mirroring: `AIReturn.ErrorMessage` is mirrored into `AIReturn.Messages` for visibility with origin and severity.

## Aggregation flow

Expand All @@ -21,8 +20,7 @@ This document explains the centralized propagation and aggregation of structured
- Messages from all interactions: `AIInteractionToolResult.Messages` and `AIInteractionImage.Messages`.
- Deduplicates by message text while preserving severity and origin.
- `AIReturn.Messages` aggregates:
- Private structured messages added during processing.
- Error mirror from `ErrorMessage`.
- Private structured messages added during processing via `AddRuntimeMessage()` or `Create*Error()` methods.
- `Request.Messages` (structured validation/capability notes).
- `Body.Messages` (aggregated interaction and body validation messages).
- Performs final deduplication and severity-first ordering.
Expand All @@ -33,7 +31,8 @@ This document explains the centralized propagation and aggregation of structured
- Use `AIBody.AddInteractionToolResult(jObject, metrics, messages)` and pass the inner return's `Metrics` and `Messages`.
- On provider/tool errors:
- Standardize with `output.CreateToolError(errorText, toolCall)` (wrappers) or appropriate `AIReturn.Create*Error(...)` helpers (infra).
- Do not copy or synthesize legacy string messages; rely on `AIReturn.Messages` mirroring of `ErrorMessage`.
- All `Create*Error()` methods add structured messages directly to `Messages` collection.
- Propagate messages: `output.Messages = result.Messages` to preserve all error context.
- For image outputs:
- Attach any structured messages to `AIInteractionImage.Messages` so they flow via `AIBody.Messages`.

Expand All @@ -50,15 +49,17 @@ This document explains the centralized propagation and aggregation of structured
// Inside a tool wrapper after calling the provider
var result = await request.Exec().ConfigureAwait(false);

if (!string.IsNullOrEmpty(result.ErrorMessage))
if (!result.Success)
{
output.CreateToolError(result.ErrorMessage, toolCall);
// Propagate all structured messages from the AI call
output.Messages = result.Messages;
return output;
}

var toolResult = new JObject { ["result"] = result.Body?.GetLastInteraction(AIAgent.Assistant)?.ToString() ?? string.Empty };
var toolBody = new AIBody();
toolBody.AddInteractionToolResult(toolResult, result.Metrics, result.Messages);
var toolBody = AIBodyBuilder.Create()
.AddToolResult(toolResult, id: toolInfo?.Id, name: toolName, metrics: result.Metrics, messages: result.Messages)
.Build();

output.CreateSuccess(toolBody);
return output;
Expand Down
8 changes: 6 additions & 2 deletions docs/Providers/AICall/tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,13 @@ var call = new AIToolCall
};

var result = await call.Exec();
if (!string.IsNullOrEmpty(result.ErrorMessage))
if (!result.Success)
{
// Handle standardized error; details also appear in result.Messages
// Handle errors - all error details are in result.Messages collection
foreach (var msg in result.Messages.Where(m => m.Severity == AIRuntimeMessageSeverity.Error))
{
Console.WriteLine($"[{msg.Origin}] {msg.Message}");
}
}
else
{
Expand Down
Binary file added img/video-select-provider.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion src/SmartHopper.Components/AI/AIModelsComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,6 @@ public override async Task DoWorkAsync(CancellationToken token)

this._result["Models"] = tree;
this._result["Success"] = true;
this._result["Info"] = "Using dynamic model list from provider API";
Debug.WriteLine("[AIModelsComponent] Using dynamic model list from provider API. Total amount of models: " + apiModels.Count);
return;
}
Expand Down
22 changes: 21 additions & 1 deletion src/SmartHopper.Components/Grasshopper/GhTidyUpComponents.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,27 @@ protected override void SolveInstance(IGH_DataAccess DA)
}
else if (toolResult["success"]?.ToObject<bool>() == false)
{
this.LastErrors.Add(toolResult["error"]?.ToString() ?? "Unknown error");
// Surface all error messages from the messages array
var hasErrors = false;
if (toolResult["messages"] is JArray msgs)
{
foreach (var msg in msgs.Where(m => m["severity"]?.ToString() == "Error"))
{
var errorText = msg["message"]?.ToString();
var origin = msg["origin"]?.ToString();
if (!string.IsNullOrEmpty(errorText))
{
var prefix = !string.IsNullOrEmpty(origin) ? $"[{origin}] " : string.Empty;
this.LastErrors.Add($"{prefix}{errorText}");
hasErrors = true;
}
}
}

if (!hasErrors)
{
this.LastErrors.Add("Unknown error");
}
}
else
{
Expand Down
30 changes: 26 additions & 4 deletions src/SmartHopper.Components/Img/AIImgGenerateComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
Expand Down Expand Up @@ -239,8 +240,9 @@ public override async Task DoWorkAsync(CancellationToken token)
.ConfigureAwait(false);

// Treat missing 'success' as true (CallAiToolAsync returns direct result without 'success' on success)
// and ensure there's no 'error' key to consider it a success.
if (toolResult != null && ((toolResult["success"]?.Value<bool?>() ?? true) && string.IsNullOrEmpty(toolResult["error"]?.ToString())))
// Check for errors in messages array
var hasErrors = toolResult?["messages"] is JArray messages && messages.Any(m => m["severity"]?.ToString() == "Error");
if (toolResult != null && ((toolResult["success"]?.Value<bool?>() ?? true) && !hasErrors))
{
// Get the image result (could be URL or base64 data)
string imageResult = toolResult["result"]?.ToString() ?? string.Empty;
Expand Down Expand Up @@ -294,8 +296,28 @@ public override async Task DoWorkAsync(CancellationToken token)
}
else
{
var errorMessage = toolResult?["error"]?.ToString() ?? "Unknown error occurred";
this.AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Image generation failed: {errorMessage}");
// Surface all error messages from the messages array
var hasErrorsLocal = false;
if (toolResult?["messages"] is JArray msgs)
{
foreach (var msg in msgs.Where(m => m["severity"]?.ToString() == "Error"))
{
var errorText = msg["message"]?.ToString();
var origin = msg["origin"]?.ToString();
if (!string.IsNullOrEmpty(errorText))
{
var prefix = !string.IsNullOrEmpty(origin) ? $"[{origin}] " : string.Empty;
this.AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"{prefix}{errorText}");
hasErrorsLocal = true;
}
}
}

if (!hasErrorsLocal)
{
this.AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Image generation failed: Unknown error occurred");
}

branchResults.Add(new GH_ObjectWrapper(null));
branchRevisedPrompts.Add(new GH_String(""));
}
Expand Down
Loading
Loading