Model Runner (local LLMs) - #346
Open
mariotoffia wants to merge 175 commits into
Open
Conversation
- Introduced real DMR captures for model management and inference, including JSON outputs for model listing, inspection, and error handling. - Added detailed documentation for Docker Model Runner (DMR), covering management, inference, capabilities, and integration with Docker Compose. - Included examples for quick start, managing models, and wiring models into containers.
… support with local LLMs, including management, chat, and embeddings functionalities.
…d add model listing functionality
feat: Introduce new error code for command execution failures refactor: Remove ModelBackend struct as it is no longer needed fix: Update ModelPruneResult to include raw output for unparsed results fix: Adjust ModelRunnerEndpoint to preserve path-bearing URLs refactor: Change Backend property in ModelConfigureOptions to string for flexibility refactor: Simplify ModelPackageRequest by removing unnecessary Labels property refactor: Clean up ModelRunOptions by removing Backend and IgnoreRuntimeMemoryCheck properties test: Add security tests for ComposeModelBuilder to prevent YAML injection test: Ensure compatibility of WithExtraHost as a default interface method test: Verify cancellation propagation during Docker API model inference test: Validate Docker CLI streaming command execution and exit handling test: Implement tests for ModelApiPaths to ensure safe URL path generation test: Add tests for ModelTlsValidation to enforce strict hostname verification feat: Implement ModelApiPaths for safe URL path construction in Docker API feat: Create ModelTlsValidation for custom root CA validation in TLS connections
…er and API connection
…stances; introduce shallow copy constructors for ChatCompletionRequest and CompletionRequest. Enhance DockerApiConnection and ModelApiConnection to manage X509 certificates properly, ensuring they are disposed correctly. Update Dispose methods across services to avoid sync-over-async deadlocks by dispatching to the thread pool. Improve documentation for Docker Model Runner, marking it as a preview feature and clarifying usage patterns. Adjust README and getting-started documentation to reflect changes in async method usage and model runner integration.
- Updated README.md to clarify Docker Model Runner availability and versioning. - Modified getting-started.md to include context size requirement for DMR v1.2.1. - Updated model-runner.md to reflect async usage and correct example links. - Added unit tests for Docker CLI argument quoting to ensure proper argument handling. - Expanded coverage tests for ModelRunnerService, including store and inference methods. - Introduced MockDriverPack enhancements for better test coverage of model management. - Added validation for environment variable names used in model-runner builders.
- Implemented `ModelApiTlsHandshakeTests` to validate TLS handshake behavior with self-signed certificates and custom CA trust. - Created `InferenceModelIdTests` to ensure correct handling of inference model IDs without injecting default tags. - Introduced `ModelEnvVarsCollection` to manage environment variable tests sequentially, preventing race conditions. - Added command execution logic in `DockerCliDriverBase` for improved process management and output handling. - Developed `InferenceDtoCopy` utility for deep copying inference request DTOs via JSON serialization. - Established `InferenceModelId` struct to represent raw model IDs accurately without automatic tag injection. - Added a script for enforcing coverage thresholds post-test execution to maintain code quality.
… sub-plans) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…n on push Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…meMismatch (A3) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…not corrupted (B7) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…h (B8) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…eld) (B10) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…body instead of throwing (C11) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… (C12) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…mbiguous rows (C14) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…s (D19) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ode (D20) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… format Pre-existing tests in DockerCliComposeDriverTests and DockerCliContainerDriverTests expected the old (incorrect) double-backslash behavior for interior path backslashes. The B7 CommandLineToArgvW fix correctly preserves single interior backslashes; update assertions to match the correct output. Also apply `dotnet format whitespace` fixes to four source files flagged by make lint. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Finding 1 (C12): Wire StreamReadIdleTimeout into the PRODUCTION SSE read loop in DockerApiModelInferenceDriver.ReadBoundedLineAsync — previously the timeout was stored in ModelApiConnection but never consulted during streaming. Now ReadBoundedLineAsync accepts TimeSpan? idleTimeout and wraps each per-character read with a linked CTS when set. Behavior is opt-in: null = wait indefinitely (caller cancellation only), matching prior behavior. - Finding 4 (C12): Change idle-timeout exception from StreamParseError to EndpointUnreachable — a stalled remote is an unreachable endpoint, not a parse error. - Finding 2 (B8): Add NonStreaming_OversizedOutput_KillsChildProcessTree_ ReturnsFailed to DockerCliProgressStreamingTests: creates a sentinel file before overflowing stdout; asserts Success=false, ExitCode=-1, and that the sentinel was never cleaned up (proving Kill(entireProcessTree:true) fired before the cleanup step ran). - Finding 3 (B7): Add 4th InlineData to QuotesWindowsPathsWithoutCorruption: input "a ends\" (space triggers quoting, trailing backslash) must produce "a ends\\" — trailing backslash doubled before the closing quote. - Remove dead ReadWithIdleTimeoutAsync from ModelApiConnection (superseded by the driver-level wiring); add StreamReadIdleTimeout property to IModelApiConnection + ModelApiConnection + MockModelApiConnection; add StallingStream and SetupStreamStalling to MockModelApiConnection for the new idle-timeout tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…in FixedStreamConnection Interface gained StreamReadIdleTimeout (C12); the benchmark stub must implement it or FluentDocker.Benchmarks fails to compile. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…IComposeModelBuilder (A2,D17) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ve Docker coupling from Services (D15,D16) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ulti-dot filenames (Co-H1/Co-M1) ResourceExtract(type, target, files) queried non-recursively, so ExtractFile's lossy namespace/filename split dropped any multi-dot resource (Dockerfile.template, settings.production, archive.tar.gz, app.properties) before Include's suffix-match ever ran (Co-H1, PROVEN LIVE: empty dir, no exception). Even when matched via a recursive query, the writer scattered it into a mangled subfolder + fragment, e.g. dir/Dockerfile/template instead of dir/Dockerfile.template (Co-M1). Fix: query recursively in the files-branch, and rewrite Include's suffix-matched ResourceInfo onto the REQUESTED name (reconstructing Namespace/RelativeRootNamespace from the fully-qualified manifest name), so the requested-files path is fully deterministic regardless of ExtractFile's guess. Also add a best-effort refinement in QueryCore for the no-files recursive path (Query()/ToFile(), which has no requested name to disambiguate): a dotless ExtractFile guess beyond the query root now re-anchors to the full remainder, landing common multi-dot shapes as one file at the target root instead of a directory+fragment. Corrected the MDL-MAJ-2 comment that overclaimed the non-recursive path already worked. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…xed docker volume specs (Co-M2)
TemplateString's opt-in handleWindowsPathIfNeeded constructor flag drove ToTargetOs,
which converted every '/' to '\' (outside quoted http(s) URLs) when both
FdOs.IsWindows() and the flag were true. A mixed docker volume spec like
c:/data:/data (Windows host : Unix container) would become c:\data:\data,
corrupting the container-side path. The flag had zero callers inside the library
(grep-verified across FluentDocker/, FluentDocker.Tests/, FluentDocker.Benchmarks/,
and Examples/) - dead flexibility exposed as a public footgun. v3.2.0-preview.2 is
not yet on NuGet, so removing the parameter is a clean pre-release change.
Deleted the flag, the constructor param, ToTargetOs, and the now-orphaned
UrlDetector/MyRegex() regex machinery that existed solely to support it. Render
now runs directly on the input string on every call, on every OS. Removed the one
test that exercised the dead flag (its assertion was the corruption itself) and
added two regression guards: a literal c:/data:/data round-trip, and a
${TMP}/data:/data host-mount spec whose container side keeps forward slashes.
No runtime RED was possible on this non-Windows machine (the flag defaulted to
false and every real call site used the 1-arg constructor already), so the proof
is structural: the whole solution + ~5.4k tests still build and pass with the
2-arg constructor gone, confirming no caller relied on it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…c aggregate + TTY log-sniff fallback (A-H1/A-M1/A-M2/A-M3)
A-H1 (HIGH): PullAsync("nginx:1.25", tag: null) sent fromImage=nginx%3A1.25&tag=latest -
the daemon's reference.WithTag discards the embedded tag and silently pulls
nginx:latest instead of nginx:1.25. Added TrySplitEmbeddedTag, mirroring the CLI
driver's ShouldAppendTag detection rule exactly, to split an embedded ":tag" into
fromImage/tag; a conflicting explicit tag now rejects with InvalidArgument instead of
being silently discarded.
A-M1 (MEDIUM): DockerApiServiceDriver.RemoveAsync aborted a multi-id remove on the
first failed DELETE, leaving every id after it (including live services) untouched.
Now attempts every id and aggregates failures, mirroring DockerApiSystemDriver.
PruneAsync's collect-then-fail pattern and the CLI driver's docker service rm
semantics. Split the now-524-line file into DockerApiServiceDriver.Parsing.cs
(spec builders + JSON parsing) to stay under the 500-line cap.
A-M2 (MEDIUM): below API 1.42, GetLogsAsync byte-sniffed the first frame to decide
multiplexed-vs-raw; a TTY container's binary output can coincidentally start with
stdcopy-header-shaped bytes and get misparsed as multiplexed, silently stripping fake
"headers" throughout the log. ReadDockerLogTailAsync now takes a containerId and, on
content-type gate failure, falls back to a TTY inspect (DetectTtyAsync, promoted from
DockerApiStreamDriver to the shared DockerApiDriverBase.Logs.cs) before falling back
further to the byte-sniff only when TTY can't be determined either.
A-M3 (doc): documented StreamIdleTimeout's full blast radius (applies to every
streamed response incl. /events/stats/attach, not just logs) and the per-timeout
pooled-buffer leak trade-off on both DockerApiConnectionConfig.StreamIdleTimeout and
IDockerApiDriverBuilder.WithStreamIdleTimeout. No behavior change.
make lint clean; make test: 5437/5437 passed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ore non-recursive exactness) Review found the QueryCore dotless-guess re-anchoring ran unconditionally, before the _recursive gate, so on the public recursive:false path a genuinely-nested dotless file (real folder Res/Sub/ with a file literally named "README" -> "Res.Sub.README") was wrongly re-anchored to the root and included, instead of correctly excluded — a new wrong-location silent write, the same class Co-H1 closes. Added `_recursive &&` to the re-anchoring condition: preserves the tested (always-recursive) Co-H1/Co-M1 behavior and restores non-recursive root-level-only exactness. The Include path is unaffected (it reconstructs the true manifest name independently of re-anchoring). Tests: new embedded fixture Res/Sub/README + regression test asserting a non-recursive query EXCLUDES it (RED pre-gate: wrongly re-anchored to root as "Sub.README"; GREEN after). Also pinned the documented no-files nested case (Res.Sub.Dockerfile.template collapses to a flat "Sub.Dockerfile.template" at the root). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…M1) + 602-line trims Doc-H1: swept all 8 docs/README references (plus the MsTestClassContainerFixtureBase.cs XML cross-ref) from the deprecated MsTestContainerFixtureBase to the new MsTestPerTestContainerFixtureBase, leaving exactly one deprecation footnote in docs/testing/mstest.md. Following the old docs previously yielded CS0618 on first build. Doc-M1: amended the [3.2.0-preview.2] CHANGELOG entry in place for post-dated API changes (MsTest rename/Deprecated section, ISysCtl.GetCapabilitiesAsync/IsHealthyAsync/ DefaultDriverId, IDriverRegistry.IsDisposeComplete/AbandonedDriverCount) and the full production-readiness remediation since 0902baf, grouped under Breaking/Added/Fixed/ Deprecated with scannable single-topic bullets — including the breaking TemplateString 2-arg constructor removal and the new ErrorCodes.ModelInference.Disposed (MIN_008) / ModelOperationGate.TrackedGateCount surface. Structural: trimmed docs/utilities.md, docs/testing/xunit.md, and docs/migrate-v2-to-v3/test-migration.md from 602 to 598-599 lines by reflowing hard-wrapped prose paragraphs (pure whitespace change, no content removed). make lint clean; make test: 5438/5438 passed (foreground, blocking run). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ADMEs (Doc-H1) Two package READMEs still taught the deprecated MsTestContainerFixtureBase as the primary name in their cross-package lifetime tables (the exact pattern Doc-H1 targets). Swept both the table cell and the prose sentence to MsTestPerTestContainerFixtureBase. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tials Document all 181 undocumented public members in FluentDocker/Model/Drivers/ (all ErrorCodes.cs error-code constants; every other file in the directory was already fully documented). ErrorCodes.cs exceeded 500 lines after docs, so it is split into 18 partial files, one per existing nested static class (General/Driver/Container/Image/Network/Volume/Compose/Pod/Kubernetes/ Machine/Manifest/Api/Config/Auth/Stack/Service/Model/ModelInference), with the main file retaining only the class doc and IsTransientCode. Mechanical split verified byte-identical (constant name, group, and string value) via a diff against the original file; no logic or values changed. CS1591 stays suppressed in the csproj (untouched). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Document all 148 undocumented public members in FluentDocker/Services/Impl/ (HostService + .Operations, ImageService, NetworkService, ComposeService + .Lifecycle/.Unpause, VolumeService, ContainerService + .Operations/.Lifecycle/ .Export, PodService, EngineScope). Members implementing a documented interface method use <inheritdoc />, matching the file's own existing convention; the handful of Pause/Stop/Remove/Hook overrides that unconditionally throw FluentDockerNotSupportedException got a real one-line summary plus an <exception> tag instead, since inheriting the base "removes the service" wording would misdescribe them. ContainerService.cs exceeded 500 lines after docs, so the inspect-cache subsystem (InspectAsync + its cache fields/record/ helper) was split into a new ContainerService.Inspect.cs partial, mirroring the file's existing .Export.cs/.Lifecycle.cs/.Operations.cs convention. No behavior change; CS1591 stays suppressed in the csproj (untouched). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Document all 123 undocumented public/protected members under FluentDocker/Drivers/ (Docker CLI driver base + components, Podman CLI driver base + components, the ISystemDriver result types, IPodmanManifestDriver's OCI-shaped result types, and the few remaining gaps in the otherwise-documented Docker API/Models areas). Most flagged members were base-class helpers (error/failure-code classification, argument quoting, process execution overloads), DTO properties mirroring Docker/ Podman CLI JSON output, or driver constructors rather than port-interface implementations, so real behavioral docs were written throughout; <inheritdoc /> was used only for the 2 PopulateMeta() overrides whose base contract is unchanged. DockerCliComposeDriver.Info.cs, ModelJsonParser.cs, and PodmanCliDriverBase.cs exceeded 500 lines after docs, so each is split via an additional partial file following the existing convention (Create.cs, Tables.cs, Execution.cs — the latter mirrors the Docker-side driver base's existing Execution.cs). No behavior change; CS1591 stays suppressed in the csproj (untouched). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…s DTOs Task 16 batch 16d: document all 108 previously-undocumented public members across Model/Containers, Model/Events, Model/Networks, Model/Images, and Model/Stacks (CS1591 stays suppressed at the csproj level; enforcement is a separate final step). Docs name the Docker/Podman CLI or JSON concept each member maps to; no behavior change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Task 16e: documents 67 CS1591-flagged public members across Model/Compose, Model/Common, Model/Builders, Model/Models, and top-level Model/*.cs. Docs name the compose YAML key or Docker concept each member maps to; also relocates a doc comment on ModelRunOptions.ContextSize that had drifted onto its private backing field. No behavior change; CS1591 suppression untouched. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…pass
Documents the last 77 undocumented public members of Task 16's full XML-doc
pass, across FluentDocker/{Resources,Testing,Common,Builders,Extensions}/:
resource query/reader/writer types, OrphanCleanup/SessionLabel test-cleanup
API, lenient/tolerant JSON converters, lifecycle-hook/network-alias builder
models, and extension-class summaries. A fresh whole-project CS1591 probe
now shows zero warnings anywhere in FluentDocker/, confirming this is the
last outstanding batch.
Testing/Core/OrphanCleanup.cs (499 lines pre-docs) is split into
OrphanCleanup.cs (public surface), OrphanCleanup.Resources.cs (per-kind
sweep logic), OrphanCleanup.Filters.cs (shared predicates), and a new
SessionLabel.cs (its own top-level type, extracted rather than left
crammed into the same file) — all four ≤ 500 lines, verified byte-for-byte
equivalent to the original via a code-line diff.
Also fixes a pre-existing broken XML doc cref in Builders/Builder.Models.cs
(missing `using FluentDocker.Kernel;`) found while probing the same
directory.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Task 16 final step: remove the blanket CS1591 suppression in FluentDocker.csproj and promote it to a build error via a scoped WarningsAsErrors (no repo-wide TreatWarningsAsErrors exists to piggyback on). A clean rebuild surfaced zero straggler undocumented members — docs batches A-F already fully covered the public API. Added the T7-deferred <exception cref="ArgumentNullException"> tags to IModelManagementDriver / IModelRuntimeDriver, verified 1:1 against the sole CLI adapter's actual ArgumentNullException.ThrowIfNull calls. Smoke-tested the gate (delete a doc -> build fails with CS1591 as an error -> restore -> build succeeds) and added a CHANGELOG line. make check: lint clean, 5438/5438 unit tests, 13+4 runner tests, coverage 87.53%/79.1% (floor 78%/71%) - all green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- PodmanMachineNotRunningException: add a (message, isTransient) overload so the P-M2 ambiguous-machine auto-start guard (multiple machines, none default) can report itself as a permanent config error instead of inheriting the hardcoded IsTransient=true meant for stopped-machine retries. Only that one throw site now passes isTransient: false; all other throw sites are unchanged. - LenientInt32Converter: reword the class summary, which claimed 0 was returned for "unparsable runtime drift" while Read() actually throws JsonException for any unrecognized token (0 is only for JSON null), matching the honest style of the sibling LenientBoolConverter. - ErrorCodes.Machine.NotRunning: note the one non-transient exception (the P-M2 ambiguous-machine case) alongside the existing transient guidance. - Pin the P-M2 test with Assert.False(ex.IsTransient). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ents - Improve kernel registration-rollback dispose to be budget-bounded. - Add IsTracked method to ModelOperationGate for per-key diagnostics. - Update Docker CLI command timeout messages for clarity. - Refactor tests to ensure proper handling of consecutive failed retries and named volume cleanup. - Ensure ModelOperationGate correctly evicts idle gates and prevents memory leaks. - Adjust assertions in tests for better accuracy and reliability.
- Updated ResourceQuery to clarify resource name extraction and improve assembly loading logic. - Refactored ResourceReader to implement a custom enumerator for better resource stream handling. - Improved ServiceEndpointResolver to ensure custom resolvers are consulted for portless containers. - Enhanced ServiceExtensions to provide better error handling and diagnostics during process waits. - Clarified documentation regarding volume deletion behavior on container removal. - Modified ComposeService to ensure hooks are executed only after confirming the project is running. - Improved ContainerService lifecycle management to handle state transitions more accurately. - Updated HostService to better manage image pulling and conflict resolution with digest references. - Enhanced ModelService to ensure proper disposal handling and state management. - Improved TopologyResource to generate unique resource names for parallel deployments. - Added internal AI-remediation prompt templates for production readiness checks. - Updated README to clarify package changes and migration guidance for users transitioning from Ductus.FluentDocker 2.x.
- Implemented tests for DockerCliDriverPack to ensure supported interfaces are cached after initialization. - Created a base class DockerCliFakeDockerTestBase for shared test functionality across Docker CLI tests. - Added tests for DockerCliImageDriver to validate image pull progress, exit code propagation, and default tag handling. - Developed tests for DockerCliModelManagementDriver and DockerCliModelRuntimeDriver to check for buffered-timeout behavior and error classification during model operations. - Introduced tests for DockerCliNetworkDriver to verify empty inspect results and IP range flag usage. - Added tests for DockerCliServiceDriver to ensure task filters, quiet projections, and log merging functionality. - Created tests for DockerCliStackDriver to validate stack name preservation and filter translations. - Implemented tests for DockerCliStreamDriver to check stderr filtering, stdin handling, and logging of malformed event lines. - Added tests for DockerCliSystemDriver to ensure context overrides, error detail preservation, and cancellation behavior. - Developed tests for DockerCliVolumeDriver to surface dedicated error codes for volume list failures.
- Implemented DockerApiImageDriver with methods for saving, loading, and importing images. - Introduced DockerApiConnection with transport handlers for Unix sockets and named pipes. - Created ServiceModels for service creation and update configurations. - Added wait helpers for container services to handle transient failures and logging. - Enhanced ContainerService with hooks for state transitions and lifecycle management. - Developed ModelService lifecycle management, including disposal and state updates.
…via '#nullable disable warnings' Flip FluentDocker.csproj to <Nullable>enable</Nullable> and prepend '#nullable disable warnings' to the 223 files that carry nullable debt. Annotation context stays on (existing ? annotations preserved, no CS8632), warnings suppressed per-file — identical to the old project-wide <Nullable>annotations</Nullable> behavior, but now every new file and every un-opted file is fully null-checked. The 298 already-clean files are enabled for free. Build 0/0 (lib + full solution). Migration is now the incremental act of removing a file's opt-out and fixing its CS86xx errors. Part of PKG-4. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Enable full nullable checking on the leaf model/DTO types (ComposeModels, ServiceModels, ApiModels, DockerSystemModels, BuilderModels) and every driver/ service port interface + connection config. Daemon-deserialized fields become truthful T?; required-positional CLI args now null-check centrally in QuotePositionalArgument. VersionInfo base version strings are T? so derived DockerVersionInfo can ??= them. Build 0/0. Part of PKG-4. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… binary (46 files + cascades) Enable full nullable checking on Docker API/CLI + Podman CLI connection, driver-base, driver-pack and binary-resolver infrastructure. Root-cause fix: SimpleCommandResult.Output/Error are non-null (= string.Empty) — command output is empty, never null — which eliminates ~24 downstream cascades. Driver-pack TryResolve out-params use [NotNullWhen(true)] out object?. TLS cert-path derefs guarded by ValidateCertificatePath. Build 0/0. Part of PKG-4. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…man CLI (63 files + cascades) Enable full nullable checking on all driver adapter components. Root-cause fix: CommandResponse<T>.Fail(string? error, ...) — an error message is legitimately optional — which eliminated ~40 downstream cascades. Parser helpers that return null are now truthfully typed; daemon-optional args annotated at source; provable non-null spots (post-success payloads, guarded cert/image refs) use !. Build 0/0. Part of PKG-4. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…/Testing (78 files + cascades) Final migration wave: enable full nullable checking on the kernel (driver registry/builders), all fluent builders, service implementations, and the Testing/Core resource layer. Root-cause fix: CommandResponse<T>.Fail(string?) already applied; service-constructor id/name args use ! (non-null by daemon success contract). Also propagated truthful ? into the Testing.* wrapper packages' fixtures and one integration test helper. Library AND full solution build 0/0. Completes the file migration for PKG-4. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… is fully enabled With <Nullable>enable</Nullable> across the whole library, the compiler proves argument non-nullness, so CA1062 (validate public-method args) has zero hits — remove the <NoWarn>CA1062</NoWarn> and its 'suppress until nullable enabled' note from Directory.Build.props. Full solution builds 0/0 with CA1062 enforced; lint (dotnet format whitespace+style) verifies clean. Closes PKG-4. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…+ JSON-null guards Adversarial review of the migration's null-forgiving operators (Builders and Testing/Kernel layers: 0 findings) surfaced genuine false non-null contracts: - IModelRunner.DefaultModel is null when no default model is configured (a unit test already asserts null) -> return ModelReference? and drop the impl '!'. - IContainerService.ToHostExposedEndpointAsync is documented and unit-tested to return null for an unbound port -> return Task<IPEndPoint?> through the impl, the extension overload, and the sync wrapper (drop the '!'). - JSON 'null' array elements slipped through element.GetString()! as real nulls into string[] (PodmanContainerParser Cmd/Env, PodmanCliManifestDriver Features, DockerApi /top Titles+Processes) -> coalesce with ?? string.Empty. Left by design (documented deliberate invariant, single reader guards with ?? throw -> FluentDockerException, not an NRE): DockerBinariesResolver.MainDockerCli and PodmanBinariesResolver.PodmanRemote. Full solution 0/0, lint clean, 5632 unit tests pass, coverage 87.57%/79.39%. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…pport for improved diagnostics
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
First-class Docker Model Runner support (local LLMs) — and any OpenAI-compatible runner — behind the existing
Builder → WithinDriver → UseModelRunner()pattern, plus the full review-remediation pass.See CHANGELOG.md (
[3.2.0] - Unreleased) and docs/model-runner.md for the complete feature, docs/model-runner-plugins.md for non-Docker runner plugins, and docs/model-runner-compose.md for Composemodels:.Highlights
IModelRunnerfaçade over three hexagonal ports (management/runtime viadocker modelCLI; inference via OpenAI-compatible HTTP on :12434 with SSE streaming), ergonomicChatAsync/ChatStreamAsync/EmbedAsync,ModelReference/LlamaCppRuntimeFlagsvalue objects, managedIModelService,WithModel(...)container wiring, Composemodels:overlay, andGenericOpenAiModelRunnerfor any OpenAI endpoint.Review remediation (latest)
Correctness
EndpointUnreachable(MIN_003) on transport failure (was downgraded to RequestFailed).EmbeddingsRequestdeep-copied before send.ModelReferenceregistry host lowercased at parse so equal refs serialize identically.Extensibility
DockerApiModelInferenceDriver→OpenAiModelInferenceDriver, relocated toFluentDocker.Drivers.Models(generic OpenAI-over-HTTP, not Docker-specific).IModelBackendInfo(Docker reportsllama.cpp; custom packs report own/none;GenericOpenAiModelRunnerreports none) instead of a hardcoded literal.NotSupportedExceptionfor partial packs instead ofInterfaceNotSupportedException.Tests — driver transport-failure mapping, SSE split-frame reassembly, completion mid-stream fault, concurrent
ChatAsync, inference-only pack capabilities + NotSupported, mTLS handshake, CLI-injection quoting.Docs — split
model-runner.mdinto Compose + plugin pages, plugin-authoring guide, README "What's New" with a multi-turn chat demo.Breaking changes
DockerApiModelInferenceDriver→OpenAiModelInferenceDriver(namespace…Docker.Api.Components→…Drivers.Models). Source-breaking only; done while the subsystem is preview/unreleased.Verification
dotnet format whitespace+style) — clean.