iron-prow local-provider safety adapter: model-ID preflight, readiness gate, and length-bounding for lm-supply local inference.
Install
| Product | Versions |
|---|---|
| .NET |
README
iron-prow
safe-inference gateway โ every LLM call's first gate.
provider selection (frontier โจ LAN GpuStack) ยท guardrail ยท resilience โ delivered as a standardMicrosoft.Extensions.AI.IChatClient.
๋ ๊ฐ๋ ํ์: frontier/LAN ๊ฒ์ดํธ์จ์ด(A) and local-provider safety(B).
์ญํ ยท๋ฒ์ยท์ค๊ณ ๊ทผ๊ฑฐ๋ CHARTER.md ์ฐธ์กฐ.
Packages
| Package | ์ญํ |
|---|---|
IronProw.Core |
๊ฒ์ดํธ์จ์ด ๊ณ์ฝ, resilience, ์ ํ ๋ก์ง โ iyulab ๊ตฌํ ์์กด 0 |
IronProw.IronHive |
IronHive provider ์ด๋ํฐ โ OpenAI ยท Anthropic ยท GoogleAI (frontier) ยท GpuStack (LAN) |
IronProw.FluxGuard |
FluxGuard guardrail ์ด๋ํฐ (IGuard ๊ตฌํ) |
IronProw.LMSupply |
๋ก์ปฌ ์ถ๋ก ์์ ๋ํผ (length-bounding + readiness gate) |
dotnet add package IronProw.Core
dotnet add package IronProw.IronHive # frontier provider adapters
dotnet add package IronProw.FluxGuard # guardrail adapter
dotnet add package IronProw.LMSupply # local-provider safety adapter
Quick Start
A. Frontier / LAN ๊ฒ์ดํธ์จ์ด
IronHive ๊ธฐ๋ฐ frontier provider + FluxGuard guardrail์ ๋ฑ๋กํ๋ค.
DI ์ปจํ
์ด๋๊ฐ ํ์ค IChatClient๋ฅผ resolveํ๋ฉฐ, ๊ฒ์ดํธ์จ์ด๊ฐ selectionยทretryยท์
์ถ๋ ฅ ๊ฒ์ฌ๋ฅผ ์๋ ์ฒ๋ฆฌํ๋ค.
using IronProw.Core;
using IronProw.IronHive;
using IronProw.FluxGuard;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.DependencyInjection;
services.AddIronProw()
.AddIronHiveOpenAI(
id: "openai",
priority: 10,
modelId: "gpt-4o",
configure: cfg => cfg.ApiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!)
.AddIronHiveAnthropic(
id: "anthropic",
priority: 5,
modelId: "claude-opus-4-5",
configure: cfg => cfg.ApiKey = Environment.GetEnvironmentVariable("ANTHROPIC_API_KEY")!)
.UseFluxGuard(); // Standard preset (L1 regex guards, offline)
// ํ์ค Microsoft.Extensions.AI.IChatClient ๋ฐํ
IChatClient chat = host.Services.GetRequiredService<IChatClient>();
var response = await chat.GetResponseAsync("Hello");
IronProw.IronHive๋ ๋ค ๊ฐ์ง provider ์ด๋ํฐ๋ฅผ ์ ๊ณตํ๋ค:
AddIronHiveOpenAIยทAddIronHiveAnthropicยทAddIronHiveGoogleAIโ frontier (ProviderKind.Frontier)AddIronHiveGpuStackโ LAN GpuStack (ProviderKind.Lan, key-optional).cfg => cfg.BaseUrl = "http://gpustack.lan:8080"ํํ๋ก endpoint ์ง์ .
Ollama / ๊ธฐํ OpenAI-ํธํ(LMStudioยทvLLM)์ ์์ง ์ด๋ํฐ๊ฐ ์๋ค โ ironhive์ ํด๋น provider๊ฐ ์๊ธฐ ๋๋ฌธ์ด๋ค(generic OpenAI-Compatible provider ๋์ ์์ ).
UseFluxGuard() (ํ๋ผ๋ฏธํฐ ์์) ๋ Standard preset(L1 regex, offline)์ ์ ์ฉํ๋ค.
์ปค์คํ
FluxGuard ์ธ์คํด์ค๋ฅผ ์ฃผ์
ํ๋ ค๋ฉด UseFluxGuard(IFluxGuard) ์ค๋ฒ๋ก๋๋ฅผ ์ฌ์ฉํ๋ค.
fail mode: ๊ธฐ๋ณธ์ fail-closed(๋ถํ์ค verdict ์ฐจ๋จ). ๊ฐ์ฉ์ฑ์ ์ฐ์ ํ๋ ์๋น์๋ UseFluxGuard(failMode: FluxGuardFailMode.Open)์ผ๋ก opt-in โ Flagged/NeedsEscalation์ ํต๊ณผ์ํจ๋ค(์ ์๋ Block์ mode ๋ฌด๊ด ํญ์ ์ฐจ๋จ).
B. Local-provider safety (lm-supply / ONNX / DirectML)
๋ก์ปฌ ์ถ๋ก ์ iron-prow ์์ ๋ ์ด์ด๋ก ๊ฐ์ผ๋ค. lm-supply ์์ฑ์(IGeneratorModel/ITextGenerator)๋ฅผ ๊ทธ๋๋ก ๋๊ธฐ๋ฉด iron-prow๊ฐ ๋ด๋ถ GeneratorChatClient ๋ธ๋ฆฌ์ง๋ก IChatClient์ ์ ์์ํจ๋ค โ ์๋น์๊ฐ ๋ธ๋ฆฌ์ง๋ฅผ ์ง์ ์์ฑํ ํ์๊ฐ ์๋ค.
using IronProw.Core;
using IronProw.LMSupply;
using IronProw.FluxGuard;
using LMSupply.Generator;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.DependencyInjection;
// generator: lm-supply๊ฐ ๋ก๋ํ IGeneratorModel/ITextGenerator (์๋ช
์ฃผ๊ธฐ๋ ํธ์ถ์ ์์ ).
// ์: var generator = await LocalGenerator.LoadAsync("gguf:default", options, null, ct);
// probe: IReadinessProbe โ ๋ชจ๋ธ ๋ก๋ ์ํ๋ฅผ ๋ณด๊ณ ํ๋ ๊ตฌํ์ฒด.
// lm-supply GeneratorPool ๊ธฐ๋ฐ์ GeneratorPoolProbe๋ฅผ ์ฌ์ฉํ๋ค.
// GeneratorPool ์์ด LoadAsync๋ก ๋จ์ผ ๋ชจ๋ธ์ ์ง์ ๋ก๋ํ๋ ๊ฒฝ์ฐ
// (์: textree)๋ LazyReadinessProbe(() => loaded, [modelId])๋ฅผ ์ฌ์ฉํ๋ค.
services.AddIronProw()
.AddLMSupplyLocal(
id: "local-phi3",
priority: 20,
generator: generator,
probe: probe,
options: new LocalSafetyOptions { DefaultMaxOutputTokens = 1024 })
.UseFluxGuard();
IChatClient chat = host.Services.GetRequiredService<IChatClient>();
AddLMSupplyLocal์ด ์ ๊ณตํ๋ safety:
- bridge โ
GeneratorChatClient๊ฐ lm-supply ์์ฑ์๋ฅผIChatClient๋ก ์ ์(role ๋งคํ,MaxOutputTokensโMaxNewTokens, sampler/tool ์ ํ, streaming flatten). ์ด๋ฏธ ๋ธ๋ฆฌ์ง๋IChatClient๋ฅผ ๋ณด์ ํ ํธ์ถ์(์: ironhive-host)๋AddLMSupplyLocal(..., IChatClient rawClient, ...)์ค๋ฒ๋ก๋๋ฅผ ์ธ ์ ์๋ค. - model-ID preflight โ
IReadinessProbe.GetAvailableModelIdsAsync๋ก ๋ชจ๋ธ ์กด์ฌ ๊ฒ์ฆ - readiness gate โ
IReadinessProbe.IsReadyAsync๋ก ๋ก๋ ์๋ฃ ํ์ธ - length-bounding โ
LocalSafetyOptions.DefaultMaxOutputTokens(๋ฏธ์ค์ ํธ์ถ์ ์๋ ์ ์ฉ, ๊ธฐ๋ณธ 512)
๋ ๊ฐ๋ ์กฐํฉ
๋ ๊ฐ๋๋ฅผ ๊ฐ์ DI ๋ฑ๋ก์์ ์กฐํฉํ ์ ์๋ค. priority๊ฐ ๋์ provider๊ฐ ๋จผ์ ์ ํ๋๊ณ , fallback ์ ๋ฎ์ ์ฐ์ ์์ provider๋ก ๊ฐ๋ฑ๋๋ค.
services.AddIronProw()
.AddIronHiveOpenAI("openai", priority: 10, "gpt-4o",
cfg => cfg.ApiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!)
.AddLMSupplyLocal("local", priority: 20, generator, probe)
.UseFluxGuard()
.Configure(opt =>
{
opt.EnableFallback = true; // ๊ธฐ๋ณธ๊ฐ
opt.Resilience.MaxRetries = 3; // ๊ธฐ๋ณธ๊ฐ: 2
opt.Resilience.BaseDelay = TimeSpan.FromMilliseconds(300); // ๊ธฐ๋ณธ๊ฐ: 200ms
opt.OnTransition = t => // retry/fallback/exhausted ์ด๋ฒคํธ (UI ์นฉ ๋ฑ)
Console.WriteLine($"[{t.Kind}] {t.ProviderId} ({t.ProviderIndex + 1}/{t.TotalProviders})");
});
OnTransition(์ ํ)์ ๊ฐ ๊ฒ์ดํธ์จ์ด ์ ํ(retry / fallback / exhausted)๋ง๋ค ํธ์ถ๋๋ best-effort ์ฝ๋ฐฑ์ด๋ค. ์๋น์๊ฐ ์ด๋ provider๋ก ๊ฐ๋ฑ๋๋์ง UI์ ํ์(์: resilience ์นฉ)ํ ์ ์๋ค. ์ฝ๋ฐฑ์ด ๋์ง๋ ์์ธ๋ ์ผ์ผ์ง๋ฉฐ ์ถ๋ก ์ ์ ๋ ๊นจ์ง ์๋๋ค. ๋ฏธ์ค์ ์ ๋์์ ๊ธฐ์กด๊ณผ ๋์ผ(๋ฌด๋ณด๊ณ ).
Crash-fallback ์ ํ
LocalSafetyChatClient(๊ฐ๋ B)๋ IReadinessProbe๋ก ๋ก์ปฌ ์ถ๋ก ๋ถ๊ฐ๋ฅผ ๊ฐ์งํ๊ณ , ๊ฒ์ดํธ์จ์ด SelectingChatClient์ provider-level fallback์ผ๋ก ์น๊ฒฉํ๋ค. ์ด๊ฒ์ ๊ฒ์ดํธ์จ์ด ์์ค fallback(M2-4 ๋ฒ์)์ด๋ค.
์ง์ง ํ๋์จ์ด ์์ค fallback(์: ONNX GenAI DirectML โ CPU)์ upstream lm-supply์ ์ฑ
์์ด๋ค.
lm-supply 0.35.x ๊ธฐ์ค, ONNX GenAI ์์ฑ ๊ฒฝ๋ก๋ DirectML โ CPU ํด๋ฐฑ์ด ์๋ค(์๋ฒ ๋ฉ ๊ฒฝ๋กยทllama-server ๊ฒฝ๋ก๋ ๋ณด์ ). iron-prow๋ ์ด gap์ ๊ฐ์ธ์ง ์๋๋ค โ upstream์ด ์์ ๋๋ฉด iron-prow ๋ณ๊ฒฝ ์์ด ์ด๋์ด ํก์๋๋ค.
๋ ๊ฐ๋ ํ์ ์ค๊ณ
iron-prow๋ ๋ ์๋๋ฆฌ์ค๋ฅผ ๋ ๋ฆฝ์ ์ด๋ฉด์๋ ์กฐํฉ ๊ฐ๋ฅํ๊ฒ ์ปค๋ฒํ๋ค:
| ๊ฐ๋ | ์ง์ ์ | ์ฑ ์ |
|---|---|---|
| A. Frontier / LAN | AddIronHiveOpenAI ยท AddIronHiveAnthropic ยท AddIronHiveGoogleAI ยท AddIronHiveGpuStack |
provider ๋ ์ง์คํธ๋ฆฌ, ์ฐ์ ์์ ์ ํ, retry, provider-level fallback, ์ ํ ์ด๋ฒคํธ(OnTransition) |
| B. local-provider safety | AddLMSupplyLocal |
model-ID preflight, readiness gate, length-bounding, crashโfallback |
์ด๋ ํ ๊ฐ๋๋ง ๊ตฌํํ๋ฉด ์์์ ์ ๋ฐ์ ๋์น๋ค (CHARTER ยง๊ธฐ๋ฅ ํ๋ฉด).UseFluxGuard()๋ ๋ ๊ฐ๋ ๊ณตํต โ ๊ด๋ฌธ์์ ์ผ๊ด ์ ์ฉ๋๋ค.
โ ๏ธ The guard is opt-in.
AddIronProw()installs a defaultNullGuardthat allows all traffic. A gateway withoutUseFluxGuard()(or a customUseGuard(...)) performs NO input/output guardrail checks. Always register a guard in production.
See also
CHARTER.mdโ iron-prow ์ ์ฒด์ฑ, ๋ฒ์, ์์กด ๊ท์น, ๋ก๋๋งต ์ต์ปค
License
MIT
Adoption Assessment
Assess this package against its supported frameworks, license, maintenance signals and dependency graph before making it a project standard.
Related Guides & Workflows
Learn how to evaluate, integrate, or audit this package in your .NET project.