iron-prow adapter: ironhive providers as gateway candidates.
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 ์ฐธ์กฐ.
์ด๋ฏธ ์์ฒด ๋ฐฐ์ (providerยทresilienceยทguardrail)์ ๊ฐ์ง ์ฑ์ ์ด๊ด์ docs/ADOPTION.md ์ฐธ์กฐ.
Packages
| Package | ์ญํ |
|---|---|
IronProw.Core |
๊ฒ์ดํธ์จ์ด ๊ณ์ฝ, resilience, ์ ํ ๋ก์ง โ iyulab ๊ตฌํ ์์กด 0 |
IronProw.IronHive |
IronHive provider ์ด๋ํฐ โ OpenAI ยท Anthropic ยท GoogleAI (frontier) ยท GpuStack ยท OpenAI-Compatible/Ollama (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 ์ง์ .AddIronHiveOpenAICompatibleโ LAN generic OpenAI-ํธํ(OllamaยทLMStudioยทvLLMยทllama.cpp server,ProviderKind.Lan, key-optional). ํ์ค/v1API ํ๋ฉด์ ๋ ธ์ถํ๋ ์๋ํฌ์ธํธ๋ฅผ ๋์์ผ๋ก ํ๋ฉฐ ๊ธฐ๋ณธ endpoint๋ Ollama์http://localhost:11434.cfg => cfg.BaseUrl = "http://localhost:1234"(LMStudio)์ฒ๋ผ override.
services.AddIronProw()
.AddIronHiveOpenAICompatible(
id: "ollama",
priority: 20, // LAN ์ฐ์ โ frontier๋ณด๋ค ๋๊ฒ ๋๋ฉด ๋ก์ปฌ ๋จผ์ ์๋
modelId: "llama3.2",
configure: cfg => cfg.BaseUrl = "http://localhost:11434"); // key ๋ถํ์
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) - reasoning default โ
LocalSafetyOptions.DefaultReasoningEffort(๋ฏธ์ค์ ํธ์ถ์ ์๋ ์ ์ฉ, ๊ธฐ๋ณธReasoningEffort.None). thinking ๊ธฐ๋ณธ-on ๋ชจ๋ธ(Gemma 4ยทQwen3)์ ์์ ์์ฐ์ reasoning ์ ์ ๋ถ ์จ ๋น ๋ต +FinishReason.Length๋ฅผ ๋๋ ค์ค ์ ์๋ค โ ์์ ๋ํผ์ ๊ณ์ฝ์ ยซ์์ฐ์ ๋ต์ ์ด๋คยป๋ผ ๊ธฐ๋ณธ์ off. ๋ชจ๋ธ ๊ธฐ๋ณธ์ ์ ์งํ๋ ค๋ฉดnull. - reasoning ์ด๋ฐ โ ๋ธ๋ฆฌ์ง๊ฐ ํ์ค
ChatOptions.Reasoning์ lm-supplyThinkingMode๋ก ๋ฒ์ญํ๋ค(Effort.NoneโOff, ๊ทธ ์ธโOn, nullโ๋ชจ๋ธ ๊ธฐ๋ณธ). ๋ชจ๋ธ์ด ๋ธ reasoning ์TextReasoningContent๋ก ์๋ต์ ์ค๋ฆฐ๋ค(ReasoningOutput.None์ด๋ฉด ๋ฒ๋ฆผ) โ ๋น ๋ต์ด ์ ๋น์๋์ง ์๋น์๊ฐ ๋ณผ ์ ์๋ค. - ๊ณ์ธก โ
ChatResponse.ModelIdยทUsage(llama-server ์ prompt/completion ํ ํฐ; ONNX ๋ null)ยทGetService<ChatClientMetadata>()(ProviderName = "LMSupply").
๊ฒฝ๋ ๊ฒฝ๋ก โ ๋จ์ผ local provider (๊ฒ์ดํธ์จ์ด ์์ด)
ํด๋ฐฑ ๋์ 2๋ฒ์งธ provider๊ฐ ์๋ local-first ๋จ์ผ provider ์๋น์(์: textree)์๊ฒ๋ ๊ฒ์ดํธ์จ์ด์ registryยทselectionยทresilience ๋ ์ด์ด๊ฐ ์ ๋ถ inertํ๋ค. ์ด ๊ฒฝ์ฐ BuildLocalSafeClient๊ฐ ๋ธ๋ฆฌ์ง+์์ wrap๋ง ์กฐ๋ฆฝํ plain IChatClient๋ฅผ ๋ฑ๋ก ์์ด ๋ฐํํ๋ค:
using IronProw.LMSupply;
using Microsoft.Extensions.AI;
// ๊ฒ์ดํธ์จ์ด(AddIronProw/๋น๋/๋ ์ง์คํธ๋ฆฌ) ์์ด guarded local client ์ง์ ์กฐ๋ฆฝ.
IChatClient chat = LMSupplyExtensions.BuildLocalSafeClient(
generator, // lm-supply ITextGenerator (ํธ์ถ์ ์์ )
probe, // IReadinessProbe
new LocalSafetyOptions { DefaultMaxOutputTokens = 1024 }); // ์ ํ (๊ธฐ๋ณธ 512)
๋์ผํ safety(preflightยทreadiness gateยทlength-bounding)๋ฅผ ๋ฐ๋ selection/fallback/resilience ์ค๋ฒํค๋๊ฐ ์๋ค. ๋ค์ค providerยท์ฐ์ ์์ ์ ํยทprovider-level fallback์ด ํ์ํด์ง๋ฉด AddLMSupplyLocal๋ก ์ ํํ๋ค.
๋ ๊ฐ๋ ์กฐํฉ
๋ ๊ฐ๋๋ฅผ ๊ฐ์ 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.Resilience.FailureThreshold = 3; // ์ฐ์ ์คํจ Nํ๋ฉด cooldown (๊ธฐ๋ณธ๊ฐ: 3, 0 = ๋)
opt.Resilience.Cooldown = TimeSpan.FromSeconds(30); // cooldown ๋์ ํ์์ (๊ธฐ๋ณธ๊ฐ: 30s)
opt.Resilience.MaxRetryAfter = TimeSpan.FromSeconds(10); // provider ์ Retry-After ๋ฅผ ๊ธฐ๋ค๋ฆฌ๋ ์ํ (๊ธฐ๋ณธ๊ฐ: 10s)
opt.OnTransition = t => // retry/fallback/exhausted ์ด๋ฒคํธ (UI ์นฉ ๋ฑ)
Console.WriteLine($"[{t.Kind}] {t.ProviderId} ({t.ProviderIndex + 1}/{t.TotalProviders})");
});
provider ๊ฑด๊ฐ ๊ธฐ์ต(0.4.0+): ๊ฒ์ดํธ์จ์ด๋ provider ๋ณ ์ฐ์ ์คํจ๋ฅผ ๊ธฐ์ตํ๋ค. FailureThreshold ํ ์ฐ์์ผ๋ก ๊ฐ๋ฑ ๋์ ์คํจ(retry ์์งยทfallback-eligible)๊ฐ ๋๋ฉด ๊ทธ provider ๋ Cooldown ๋์ ํ์์๋ก ๋ฐ๋ฆฐ๋ค โ ์ ์ธ๊ฐ ์๋๋ผ ํ์์๋ผ ๊ฑด๊ฐํ provider ๊ฐ ํ๋๋ ์์ผ๋ฉด ์ฌ์ ํ ์๋๋๊ณ , ํ ๋ฒ ์ฑ๊ณตํ๋ฉด ๊ธฐ๋ก์ด ์ง์์ง๋ค. ๊ทธ ์ ์ ์ฃฝ์ LAN provider ๊ฐ ๋งค ํธ์ถ์ retry ์์ฐ(๊ธฐ๋ณธ 2ํ + backoff)์ ๋ฌผ๋ฆฐ ๋ค์์ผ ๋ค์ provider ๋ก ๋์ด๊ฐ๋ค. ๋ฐ๋ฆฐ provider ๋ OnTransition ์ ProwTransitionKind.Skipped ๋ก ๋ณด๊ณ ๋๋ค. EnableFallback = false ๋ฉด ๊ฑด๊ฐ ๊ธฐ์ต๋ ๋ผ์ฐํ
์ ์ฐ์ง ์๋๋ค(๊ทธ ์ค์ ์ ๋ป์ด ยซ์ ๋ provider ๋ฅผ ๋ฐ๊พธ์ง ์๋๋คยป๋ผ์). ์ฐ๊ฒฐ ๊ฑฐ๋ถยท์ด๋ฆ ํด์ ์คํจ(HttpRequestError.ConnectionError/NameResolutionError)๋ retry ๊ฐ ์๋๋ผ ์ฆ์ fallback ์ผ๋ก ๋ถ๋ฅ๋๋ค โ ์๋ํฌ์ธํธ๊ฐ ๋ฐ์ ๊ฒ ์๋๋ผ ์๋ ๊ฒ์ด๋ผ์.
HTTP ์ํ๋ณ ๋ถ๋ฅ(0.5.0+): HTTP ์คํจ๋ ์์ธ ํ์
์ด ์๋๋ผ ์ํ ์ฝ๋๋ก ๋ถ๋ฅ๋๋ค โ OpenAI SDK ์ ClientResultException, HttpRequestException.StatusCode, ironhive ์ RateLimitException(AddIronHive* ๊ฐ ๋ฑ๋กํ๋ IronHiveHttpFailureReader) ๋ชจ๋ ๊ฐ์ ๊ท์น์ด๋ค.
| ์ํ | ๋ถ๋ฅ |
|---|---|
| 408 ยท 500 ยท 502 ยท 504 | ๊ฐ์ provider ์์ retry |
| 429 ยท 503 | provider ๊ฐ retry ํํธ(Retry-After / retry-after-ms)๋ฅผ ๋ณด๋์ผ๋ฉด ๊ทธ๋งํผ ๊ธฐ๋ค๋ ค retry(MaxRetryAfter ์ด๊ณผ๋ฉด retry ์์ด ๋ค์ provider), ์์ผ๋ฉด ์ฆ์ ๋ค์ provider |
| ๊ทธ ๋ฐ(400 ยท 401 ยท 403 ยท 404 ยท 409 โฆ) | ๋ค์ provider (ํ provider ์ ๊ฑฐ์ ์ ๋ค๋ฅธ provider ๋ ๊ฑฐ์ ํ๋ค๋ ์ฆ๊ฑฐ๊ฐ ์๋๋ค) |
retry ๋ฅผ ๋ค ์ด ์คํจ๋ ๋ค์ provider ๋ก ๋์ด๊ฐ๋ค. ์ํ์ ๋๋ฉ์ธ ์๋ฏธ๋ฅผ ์ฃผ๋ provider(์: ๋ค์ด๋ก๋ ์น์ธ ์ ๊น์ง 409 ๋ฅผ ๋ด๋ ๋ก์ปฌ provider)๋ ์๋น์๊ฐ IErrorClassifier ๋ฅผ ๋ฐ์ฝ๋ ์ดํธํด ๊ทธ ์ฝ๋๋ง ๋ค๋ฅด๊ฒ ๋ถ๋ฅํ๋ค. ๋ค๋ฅธ ์์ธ ํ์
์ด HTTP ์คํจ๋ฅผ ๋๋ฅด๋ฉด IHttpFailureReader ๋ฅผ TryAddEnumerable ๋ก ๋ฑ๋กํ๋ค.
OnTransition(์ ํ)์ ๊ฐ ๊ฒ์ดํธ์จ์ด ์ ํ(retry / fallback / exhausted / skipped)๋ง๋ค ํธ์ถ๋๋ best-effort ์ฝ๋ฐฑ์ด๋ค. ์๋น์๊ฐ ์ด๋ provider๋ก ๊ฐ๋ฑ๋๋์ง UI์ ํ์(์: resilience ์นฉ)ํ ์ ์๋ค. ์ฝ๋ฐฑ์ด ๋์ง๋ ์์ธ๋ ์ผ์ผ์ง๋ฉฐ ์ถ๋ก ์ ์ ๋ ๊นจ์ง ์๋๋ค. ๋ฏธ์ค์ ์ ๋์์ ๊ธฐ์กด๊ณผ ๋์ผ(๋ฌด๋ณด๊ณ ).
์คํธ๋ฆฌ๋ฐ ๋๋ฑ์ฑ: retryยทfallback์ GetResponseAsync์ GetStreamingResponseAsync ์์ชฝ์ ๋์ผํ๊ฒ ์ ์ฉ๋๋ค. ์คํธ๋ฆฌ๋ฐ์ ๋ณต์๋ ฅ ์ฐฝ์ "์ฒซ ChatResponseUpdate๊ฐ yield๋๊ธฐ ์ " ์ด๋ค โ ์ฒซ ์ฒญํฌ ์ด์ ์ ๋ฐ์ํ ์คํจ(์: OpenAI ํธํ ํธ์ถ์ด ์ฒซ MoveNextAsync์์ ๋์ง๋ connection-refused / 404 / model-not-found)๋ same-provider retry(Retryable) ๋๋ next-provider fallback(FallbackEligible)์ผ๋ก ์ฒ๋ฆฌ๋๋ค. ์ฒซ ์ฒญํฌ๊ฐ emit๋ ๋ค์ ์คํจ๋ provider๋ฅผ ๋ฐ๊พธ๋ฉด ์ด์ค emit์ด ๋๋ฏ๋ก ๊ทธ๋๋ก ์ ํ๋๋ค.
๋ฉํฐํ ๋ํธ โ per-tenant provider resolution
๊ธฐ๋ณธ AddProvider/AddLMSupplyLocal ๊ฒฝ๋ก๋ provider ์งํฉ์ด ํ๋ก์ธ์ค ์๋ช
๋์ ๊ณ ์ ์ธ ์๋น์(๋ฐ์คํฌํ ์์ด์ ํธ, ๋จ์ผ ์ ์ )๋ฅผ ์ํ ๊ฒ์ด๋ค. ์ํฌ์คํ์ด์ค๋ง๋ค provider ์งํฉยทconfigยทsecret์ด ๋ค๋ฅธ ๋ฉํฐํ
๋ํธ ์๋ฒ ์๋น์๋ AddTenantResolver๋ก per-tenant ๊ฒ์ดํธ์จ์ด๋ฅผ ๋ฐํ์์ buildํ๋ค.
// startup โ ๋จ์ผ ํ
๋ํธ AddProvider ๊ฒฝ๋ก์ ๋ณ์กด(๋ฌดํ๊ท)
services.AddIronProw()
.UseFluxGuard()
.AddTenantResolver((sp, tenant) => // ์ ๊ท ํ๋ฉด
sp.GetRequiredService<ProviderService>() // consumer ๊ตฌํ
.ResolveRegistrations(tenant)); // per-workspace ์งํฉ โ ProviderRegistration[]
// per-request (์์ฒญ ์ค์ฝํ์์ resolve)
var client = scopedSp.GetRequiredService<IIronProwFactory>().ForTenant(workspaceId);
await client.GetResponseAsync(msgs, options, ct); // guarded: select/retry/fallback/guard
ForTenant(tenant)์ ํด๋น ํ ๋ํธ์ provider ์งํฉ์ผ๋กSelectingChatClient๋ฅผ ์ฌ์กฐ๋ฆฝํ๋ค โ selector/guard/classifier/options๋ ๊ณต์ (์ฌ์ฌ์ฉ), registry๋ง per-tenant. tenant ํค๋ iron-prow์ opaque(resolver๊ฐ ํด์).IIronProwFactory๋ scoped๋ก ๋ฑ๋ก๋๋ฏ๋ก ์์ฒญ ์ค์ฝํ์์ resolveํด์ผ resolverยทprovider factory๊ฐ ์์ฒญ ๋ฒ์ ์๋น์ค(์: ๋ณตํธํ๋ ์ํฌ์คํ์ด์ค secret)๋ฅผ ๋ณธ๋ค.- async๋ ์๋ฅ์์: resolver์
ProviderRegistration.ClientFactory๋ ๋ชจ๋ sync๋ค. ์ํฌ์คํ์ด์ค secret์ async DB ๋ก๋ยท๋ณตํธํ๋ consumer์ ์์ฒญ ๋ฏธ๋ค์จ์ด์์ ์ํํด scoped ์๋น์ค์ stashํ๊ณ , resolver๋ ๊ทธ๊ฒ์ sync๋ก ์ฝ๋๋ค. (์์ฒญ๋น async ๋ก๋๊ฐ ํ์๋ผ๋ฉด ํฅํForTenantAsync์ค๋ฒ๋ก๋๊ฐ ์์ additive๋ก ์ถ๊ฐ๋ ์ ์๋ค.) - ๋ฐํ๋ client๋ ๋งค ์์ฒญ build(์ฐ๊ฒฐ ์์ยท์ ๋น์ฉ)๋ค. consumer๊ฐ provider factory์์
HttpClient๋ฑ disposable์ ์ฅ๋ฉด ์๋ช ์ consumer ์ฑ ์์ด๋ค. - ๋จ์ผ ํ
๋ํธ ๊ฒฝ๋ก(
AddProvider/AddLMSupplyLocal, singletonIChatClient)๋ ์์ ๋ฌด๋ณ๊ฒฝ์ผ๋ก ๋ณ์กดํ๋ค.
๋ฐ๋ณต ํดํ ์ ์ง (0.6.0+, opt-in)
์์ ๋ก์ปฌ ๋ชจ๋ธ์ ๊ฐ์ ๋จ์ดยท๊ตฌ์ ์ ํ ํฐ ์ํ๊น์ง ๋ฐ๋ณตํ๋ ํดํ์ ๋น ์ง๊ณค ํ๋ค("concisely concisely concisely โฆ"). WithDegenerationStop()์ ์ด๋ค IChatClient๋ (๊ฒ์ดํธ์จ์ด ํฌํจ) ๊ฐ์ธ์, ์ถ๋ ฅ ๋์ด ์งง์ ๋จ์(โค 60์, ๊ธ์ ํฌํจ)์ 4ํ ์ด์ ์ฐ์ ๋ฐ๋ณต์ด ๋๋ฉด ์์ฑ์ ๋ฉ์ถ๋ค. ๊ฒ์ฌ ๋์์ ์ถ๋ ฅ์ ๋ง์ง๋ง 240์๋ค. Markdown ๊ตฌ์กฐ์ ๊ตฌ๋์ ์ฐ์(----, | --- |, ====, ....)์ ๋ฐ๋ณต์ผ๋ก ๋ณด์ง ์๋๋ค. ํ์ ์ ์ข์ ๋ต์ ๋์ง ์๋ ์ชฝ์ผ๋ก ์น์ฐ์ณ ์๋ค.
IChatClient chat = sp.GetRequiredService<IChatClient>().WithDegenerationStop(o => o.MinRepeats = 4);
// ChatClientBuilder ์์๋: builder.Use(inner => new DegenerationStopChatClient(inner))
๋ฉ์ถค์ ์กฐ์ฉํ ๋์ด ์๋๋ผ ์ ํธ๋ค. ์คํธ๋ฆฌ๋ฐ์ ๋ง์ง๋ง ์
๋ฐ์ดํธ์ ๋น์คํธ๋ฆฌ๋ฐ ์๋ต์ FinishReason์ด DegenerationStopChatClient.FinishReason("degeneration")์ด ๋๊ณ , ๋ฐ๋ณต๋ ๋จ์๋ AdditionalProperties[DegenerationStopChatClient.RepeatedUnitKey]์ ์ค๋ฆฐ๋ค. ์ด๋ฏธ ๋ด๋ณด๋ธ ํ
์คํธ๋ ๊ทธ๋๋ก ๋๊ณ , ์์ชฝ ์คํธ๋ฆผ์ ๋ฒ๋ ค ์์ฑ์ ์ทจ์ํ๋ค. ๋น์คํธ๋ฆฌ๋ฐ ํธ์ถ๋ ์์ชฝ ์คํธ๋ฆผ์ผ๋ก ๋ฐ์ ์กฐ๊ธฐ์ ๋ฉ์ถ๋ค. ํ์ ๋ง ํ์ํ๋ฉด DegenerationDetector.FindRepeatingUnit(text)๋ฅผ ์ด๋ค.
์๋ฐฉ ์ชฝ์ ๋ก์ปฌ ๊ฒฝ๋ก(IronProw.LMSupply)์ ์ํ๋ฌ๋ค. ChatOptions.FrequencyPenalty/PresencePenalty๊ฐ ์ ๋ฌ๋๊ณ , lm-supply ๊ณ ์ ์ repetition_penalty(๊ธฐ๋ณธ 1.1)๋ ChatOptions.AdditionalProperties["repetition_penalty"]๋ก ์ค๋ค.
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 ยท AddIronHiveOpenAICompatible |
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
Changes from Previous Version
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.