Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
74af37d
fix(stats): break BasicStats.CalculateStats recursion that crashed te…
ooples Apr 18, 2026
f43da33
test(data): cross-platform retry trigger for RobustFileOps tests
ooples Apr 18, 2026
abcdcdd
fix(serialization): match MultiHeadAttentionLayer 5-arg constructor i…
ooples Apr 18, 2026
d61cc69
fix(optimizer): re-allocate Adam moments when cached shape mismatches…
ooples Apr 18, 2026
a38e3a0
fix(serving): cross-platform sanitizer for AesGcm artifact filenames
ooples Apr 18, 2026
d9eeebe
fix(layers): sparselinearlayer reports supportstraining true
ooples Apr 18, 2026
d413083
perf(dit): vectorize Patchify/Unpatchify/AdaLN via Engine reshape+per…
ooples Apr 18, 2026
f7db4da
perf(init): batched parallel Xavier normal weight initialization
ooples Apr 18, 2026
c22c3f6
chore(deps): bump aidotnet.tensors 0.46.0 -> 0.46.1
ooples Apr 18, 2026
05f3a03
fix(stats): break EnsureFullStatsComputed recursion in errorstats/mod…
ooples Apr 18, 2026
0f1bb6f
fix(networks): resnet/vgg train adds batch dim for 3d input
ooples Apr 18, 2026
2c5b83e
fix(networks): mobilenetv2 handles 3d input in forward/train/namedact…
ooples Apr 18, 2026
46f2f2f
test(networks): instructorembedding test shape matches 768-dim model
ooples Apr 18, 2026
08b86e7
fix(networks): convolutionalneuralnetwork train adds batch dim for 3d…
ooples Apr 18, 2026
15dc66c
fix(networks): unet3d decoder channel count + test output shape
ooples Apr 18, 2026
b046eb2
fix(gp): escalating cholesky jitter for sparsegaussianprocess.fit
ooples Apr 18, 2026
290090d
fix(generators): correct audio/video modeldomain ordinal in testscaff…
ooples Apr 18, 2026
42b48ad
test(neuralnetworks): align word2vec test shapes with softmax vocab head
ooples Apr 18, 2026
ca646c8
test(ner): emit 768-dim scaffolded shapes for transformer ner models
ooples Apr 18, 2026
6173705
fix(layers): default rnn head should use identityactivation, not relu…
ooples Apr 19, 2026
d964ea4
fix(memorynetwork): seed memory and wire training through the memory-…
ooples Apr 19, 2026
0cf7780
fix(quantumnn): migrate training to trainwithtape and use identity on…
ooples Apr 19, 2026
9c5d698
fix(diffusion): upscaleavideo inputconv should match latent channels,…
ooples Apr 19, 2026
dee79b6
fix(diffusion): videounet spatial resblock must mix channels, not width
ooples Apr 19, 2026
e798e07
fix(serialization): register memoryread and memorywrite layers for de…
ooples Apr 19, 2026
567aebc
fix(gp): sparsegp ky solve falls back to svd pseudoinverse when chole…
ooples Apr 19, 2026
1d90efe
fix(regression): poisson irls must not overwrite coefficients with na…
ooples Apr 19, 2026
44be1ff
fix(regression): rbf solve via tikhonov-damped svd instead of normal-…
ooples Apr 19, 2026
4fcea5c
fix: address 10 CodeRabbit review comments on PR #1156
franklinic Apr 19, 2026
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
2 changes: 1 addition & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<ItemGroup>
<!-- AiDotNet ecosystem -->
<PackageVersion Include="AiDotNet" Version="0.113.0" />
<PackageVersion Include="AiDotNet.Tensors" Version="0.46.0" />
<PackageVersion Include="AiDotNet.Tensors" Version="0.46.1" />
Comment thread
ooples marked this conversation as resolved.
<PackageVersion Include="AiDotNet.Native.OneDNN" Version="0.38.0" />
<PackageVersion Include="AiDotNet.Native.OpenBLAS" Version="0.28.0" />
<PackageVersion Include="AiDotNet.Native.CLBlast" Version="0.37.0" />
Expand Down
33 changes: 29 additions & 4 deletions src/AiDotNet.Generators/TestScaffoldGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1492,14 +1492,21 @@ private static void EmitGeneratedTestClass(
}
}
}
else if (model.Domains.Contains(3) && !model.Tasks.Contains(35))
else if (model.Domains.Contains(4) && !model.Tasks.Contains(35))
{
// Temporal video models (ActionRecognition=22, VideoGeneration=41, etc.)
// need a 4D [frames, channels, height, width] input shape, but
// NeuralNetworkArchitecture only expresses 3D (height/width/depth).
// Rather than silently emit a mismatched 3D architecture alongside a
// 4D InputShape, route these to a runtime placeholder until the
// architecture type can represent a temporal dimension.
//
// The enum ordinal for ModelDomain.Video is 4
// (General=0, Vision=1, Language=2, Audio=3, Video=4, ...).
// This check previously used 3, which incorrectly flagged every
// *audio* model (PlayHT, Bark, etc.) as "temporal video" and
// emitted a NotImplementedException factory — ten PlayHTTests
// failures on PR #1156 traced to this off-by-one.
constructorExpr = "throw new System.NotImplementedException(" +
$"\"'{GeneratorHelpers.StripGenericSuffix(model.ClassName)}' is a temporal video model; NeuralNetworkArchitecture<T> cannot express its 4D [frames, channels, height, width] input. Implement this factory manually.\")";
}
Expand All @@ -1510,7 +1517,7 @@ private static void EmitGeneratedTestClass(
// others default to OneDimensional. Temporal video is handled above.
needsArchitectureUsing = true;
bool isVision = model.Domains.Contains(1) || model.Domains.Contains(11); // Vision=1, ThreeD=11
bool isAudio = model.Domains.Contains(4); // Audio=4
bool isAudio = model.Domains.Contains(3); // Audio=3 (enum ordinal, not Video=4)
bool isFrameInterp = model.Tasks.Contains(35); // FrameInterpolation → 3D input

string inputTypeExpr;
Expand Down Expand Up @@ -1623,11 +1630,12 @@ private static void EmitGeneratedTestClass(

// Override InputShape/OutputShape for domain-appropriate test data.
// Vision/Video/3D models need [C, H, W]; default is [1, 4].
bool isVideoModel = model.Domains.Contains(3);
// Enum ordinals: General=0, Vision=1, Language=2, Audio=3, Video=4.
bool isVideoModel = model.Domains.Contains(4); // Video=4 (was incorrectly 3)
bool isFrameInterpModel = model.Tasks.Contains(35); // FrameInterpolation
bool isTemporalVideoModel = isVideoModel && !isFrameInterpModel;
bool isVisionModel = model.Domains.Contains(1) || model.Domains.Contains(11);
bool isAudioModel = model.Domains.Contains(4);
bool isAudioModel = model.Domains.Contains(3); // Audio=3 (was incorrectly 4)
if (isTemporalVideoModel)
{
// Temporal video: [frames, channels, height, width]
Expand Down Expand Up @@ -1660,6 +1668,23 @@ private static void EmitGeneratedTestClass(
sb.AppendLine($" protected override int[] InputShape => new[] {{ {dim} }};");
sb.AppendLine(" protected override int[] OutputShape => new[] { 4 };");
}
else if (family == TestFamily.TransformerNER || family == TestFamily.SpanBasedNER)
{
// TransformerNERBase and SpanBasedNERBase both default to
// HiddenDimension=768 (BERT-base). Inputs are validated as
// [seqLen, 768], so the base-class default [1, 4] causes a
// hard "embedding dim mismatch" failure inside MultiHeadAttention
// before any downstream logic runs. Use a short sequence to
// keep the test fast while matching the model's expected
// embedding size. Models with non-default hidden dimensions
// (TinyBERT=312, etc.) need a manual test override.
sb.AppendLine(" protected override int[] InputShape => new[] { 8, 768 };");
}
else if (family == TestFamily.SequenceLabelingNER)
{
// LSTM-CRF family defaults to EmbeddingDimension=100.
sb.AppendLine(" protected override int[] InputShape => new[] { 8, 100 };");
}

sb.AppendLine($" protected override {returnTypeCode} {factoryMethodName}()");
sb.AppendLine(factoryBody);
Expand Down
62 changes: 59 additions & 3 deletions src/AiDotNet.Serving/Services/AesGcmModelArtifactProtector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,67 @@ public ProtectedModelArtifact ProtectToFile(string modelName, string sourcePath,
}
}

/// <summary>
/// Cross-platform-invalid filename characters. Combines the Windows
/// invalid set (most restrictive: ":" + "\\" + reserved punctuation +
/// control chars) with POSIX "/" and "\0". Used instead of
/// <see cref="Path.GetInvalidFileNameChars"/> because that method
/// returns a platform-specific set — on Linux it only contains '\0'
/// and '/', so a model name like "my:model" sanitizes to "my:model"
/// on Linux but "my_model" on Windows. Encrypted artifacts are
/// designed to be portable, so we apply the strict Windows superset
/// on every OS to guarantee the output is mountable everywhere.
/// </summary>
private static readonly HashSet<char> CrossPlatformInvalidFileNameChars =
new(new[]
{
'\0', '/', '\\', ':', '*', '?', '"', '<', '>', '|',
}
.Concat(Enumerable.Range(1, 31).Select(i => (char)i)));

/// <summary>
/// DOS reserved device names. Creating a file with any of these as the
/// base name (with or without extension) fails on Windows with
/// <c>PathTooLongException</c> / <c>IOException</c> because the kernel
/// still routes them to legacy character devices. Cross-platform
/// portability requires rejecting them even on POSIX hosts so an
/// artifact produced on Linux can't be loaded on Windows.
/// </summary>
private static readonly HashSet<string> WindowsReservedFileNames =
new(StringComparer.OrdinalIgnoreCase)
{
"CON", "PRN", "AUX", "NUL",
"COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
"LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
};

private static string SanitizeFileName(string name)
{
var invalid = Path.GetInvalidFileNameChars();
var chars = name.Select(c => invalid.Contains(c) ? '_' : c).ToArray();
return new string(chars);
// 1. Replace cross-platform-invalid characters.
var chars = name.Select(c => CrossPlatformInvalidFileNameChars.Contains(c) ? '_' : c).ToArray();
var sanitized = new string(chars);

// 2. Windows strips trailing dots and spaces from filenames at create-time
// (so "model." silently becomes "model", but "model." on some paths fails
// with PathNotFound). Trim on every platform to avoid the mismatch.
sanitized = sanitized.TrimEnd(' ', '.');

// 3. If the base (pre-extension) is a reserved DOS device name, prefix it
// so the artifact remains portable. Split on the first dot so "NUL.bin"
// also gets rewritten.
if (sanitized.Length == 0)
{
return "_";
}

var dotIndex = sanitized.IndexOf('.');
var baseName = dotIndex >= 0 ? sanitized.Substring(0, dotIndex) : sanitized;
if (WindowsReservedFileNames.Contains(baseName))
{
sanitized = "_" + sanitized;
}

return sanitized;
}
}

Loading
Loading