Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions src/Netclaw.Cli.Tests/Model/ModelCommandTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,11 @@ public async Task Set_ClearModalities_RemovesExistingOverride()
[InlineData("--input-modalities requires a value", "--input-modalities")]
[InlineData("unknown argument '--input-modalites'", "--input-modalites", "Text")]
[InlineData("invalid modalities", "--input-modalities", "3")]
[InlineData("invalid modalities", "--input-modalities", "1")]
[InlineData("invalid modalities", "--input-modalities", "2")]
[InlineData("invalid modalities", "--input-modalities", "4")]
[InlineData("invalid modalities", "--input-modalities", "8")]
[InlineData("invalid modalities", "--output-modalities", "1")]
[InlineData("cannot be combined", "--context-window", "32768", "--clear-context-window")]
public async Task Set_InvalidOptions_ReturnErrorWithoutWriting(
string expectedError,
Expand Down
11 changes: 6 additions & 5 deletions src/Netclaw.Cli/Model/ModelCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// Copyright (C) 2026 - 2026 Petabridge, LLC <https://petabridge.com>
// </copyright>
// -----------------------------------------------------------------------
using System.Globalization;
using System.Text.Json;
using Microsoft.Extensions.Configuration;
using Netclaw.Cli.Config;
Expand Down Expand Up @@ -306,11 +307,11 @@ private static bool TryParseModalities(string value, out ModelModality modalitie
var result = ModelModality.None;
foreach (var token in value.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
{
// Enum.TryParse also accepts the underlying integer ("3" → Text|Image), but the contract
// advertised in the help text and error message is named flags only. Enum.IsDefined
// rejects a numeric token because its parsed value is not a single declared member, so a
// mistyped or scripted number is not silently coerced into a modality set.
if (!Enum.TryParse(token, ignoreCase: true, out ModelModality parsed)
// Enum.TryParse accepts underlying integers, including values that happen to be declared
// members ("1" → Text). The CLI contract is named flags only, so reject numeric tokens
// before parsing rather than relying on Enum.IsDefined to distinguish composites.
if (int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out _)
|| !Enum.TryParse(token, ignoreCase: true, out ModelModality parsed)
|| !Enum.IsDefined(parsed)
|| parsed == ModelModality.None)
return false;
Expand Down
Loading