UVision.Hosting
0.4.0
dotnet add package UVision.Hosting --version 0.4.0
NuGet\Install-Package UVision.Hosting -Version 0.4.0
<PackageReference Include="UVision.Hosting" Version="0.4.0" />
<PackageVersion Include="UVision.Hosting" Version="0.4.0" />
<PackageReference Include="UVision.Hosting" />
paket add UVision.Hosting --version 0.4.0
#r "nuget: UVision.Hosting, 0.4.0"
#:package UVision.Hosting@0.4.0
#addin nuget:?package=UVision.Hosting&version=0.4.0
#tool nuget:?package=UVision.Hosting&version=0.4.0
UVision.Hosting
제조 현장용 AI 비전 검사 PWA + API를 ASP.NET Core 앱으로 띄우는 라이브러리. 태블릿 카메라가 정지 감지로 자동 촬영 → VLM이 시나리오 기준으로 OK/NG 판정. 빌드된 PWA가 패키지(어셈블리)에 임베드되어 있어 별도 정적 파일 배포가 필요 없다.
- PWA 가
BasePath(기본/u-vision)에 마운트 - API 가
ApiBasePath(기본/api/u-vision)에 마운트 - 기본 VLM provider 는
mock→ API 키 없이 바로 동작(연동 확인용) - 단일 조직 셀프호스트 전제(멀티테넌시·계정 시스템 없음)
패턴:
AddUVision()+UseUVision()+MapUVisionEndpoints(). 단독 서버(server/src/UVision.Api)가 이 세 줄로 지어진다.
어떻게 연동하나 — 엔진은 별도 앱으로
U-Vision 은 자기 프로세스에서 도는 엔진 서버로 쓰는 것이 기본이다. 다른 시스템(MES · 업무 앱)은 그 서버의 HTTP API(OpenAPI 계약)와 판정 사건 웹훅으로 붙고, 화면이 필요하면 호스트의 리버스 프록시가 PWA 경로를 같은 출처로 실어 준다(samples/SameOriginMount). 프로세스 경계는 라이선스 경계이기도 하다(아래 «라이선스»).
이 패키지를 기존 업무 앱의 프로세스 안에 넣는 방식은 이미 그렇게 쓰는 소비자를 위한 이행기 호환 경로다 — 새 연동에는 권하지 않는다. 옮겨 갈 수단은 엔진 서버 + UVision.Client(계약에서 생성한 HTTP 클라이언트) + 같은 출처 마운트이고, 지금 이 방식으로 쓰는 소비자가 옮긴 판의 다음 minor 에서 없앤다(CHANGELOG 로 알린다).
설치
dotnet add package UVision.Hosting
최소 통합 (3줄 와이어링)
Program.cs:
using UVision.Api;
using UVision.Api.Endpoints;
var builder = WebApplication.CreateBuilder(args);
// 1) 코어 서비스 등록 — 설정 섹션 "UVision"에서 바인딩, 스토리지를 contentRoot 아래로 절대화.
builder.Services.AddUVision(builder.Configuration, builder.Environment.ContentRootPath);
var app = builder.Build();
// 2) PWA(BasePath) + 정적 에셋 + 런타임 config 주입. ※ 호스트의 라우팅/엔드포인트보다 먼저 둔다.
app.UseUVision();
// 3) API(ApiBasePath) 엔드포인트 매핑.
app.MapUVisionEndpoints(app.Services.GetRequiredService<UVisionOptions>().ApiBasePath);
app.Run();
설정 없이도 동작한다(기본값: provider mock, 스토리지 ./data, PWA /u-vision, API /api/u-vision). 브라우저로 https://<host>/u-vision/ 접속 → PWA 로드. GET /api/u-vision/health → {"status":"ok","provider":"mock"}.
getUserMedia(카메라)·Wake Lock 은 secure context 필수 — 운영 시 HTTPS(또는localhost)로 서빙한다.
전체 동작 예제: 단독 서버 server/src/UVision.Api/Program.cs. 다른 앱에서 엔진을 부르는 예제는 samples/ClientIntegration(UVision.Client).
설정 (config 섹션 UVision)
appsettings.json:
{
"UVision": {
"Vlm": {
"Provider": "mock", // mock | openai-compatible | openai | google
"Model": "gpt-4o",
"ApiKey": "", // openai/google
"Endpoint": "", // openai-compatible 서버 주소(/v1 은 자동으로 붙는다)
"MaxUploadSizeMb": 10
},
"Ml": {
"Provider": "none", // none(VLM 단독) | mock | mloop — 전용 ML 분류기(신뢰성 플라이휠 ②~③)
"Endpoint": "", // mloop serve base URL(예: http://host:5000)
"Model": "default", // 학습 모델 이름
"Token": "", // mloop serve JWT Bearer
"ImageColumn": "ImagePath",
"ReviewConfidenceThreshold": 0.0 // ③ 2중체크: VLM/ML 신뢰도가 이 값 미만이면 requires_review(0=불일치만)
},
"Storage": {
"DataPath": "data", // 상대경로는 호스트 ContentRoot 기준
"IdempotencyRetentionHours": 168 // Idempotency-Key 재전송 응답 보존 시간(0 이하 = 지우지 않음)
},
"Events": { // 판정 사건 웹훅(CloudEvents · Standard Webhooks 서명) — 비우면 비활성
"Webhooks": [ { "Name": "mes", "Url": "https://receiver.example/hook", "Secret": "whsec_..." } ]
},
"AdminPin": null, // 미설정 시 관리(쓰기)는 503 + 파일시스템 부트스트랩으로 최초 설정(아래 참고)
"BasePath": "/u-vision", // PWA 마운트 경로 (⚠️ 아래 제약 참고)
"ApiBasePath": "/api/u-vision", // API 네임스페이스
"Title": "U-Vision" // 런타임 config로 SPA에 주입
}
}
실제 VLM provider 연동 예(OpenAI 호환 셀프호스트 서버 — GPUStack · vLLM · Ollama 등):
"Vlm": { "Provider": "openai-compatible", "Model": "qwen2.5-vl", "Endpoint": "http://gpu-host:8080" }
③ 2중체크 (VLM + 전용 ML 교차검증)
Ml.Provider 가 none 이 아니면 /inspect 가 VLM·ML 을 병렬 호출(Task.WhenAll)해
교차검증하고, 응답·저장 레코드에 additive 필드를 덧붙인다(ML none이면 필드 전체 생략 = 기존 wire 보존):
{
"verdict": "OK", "findings": "", "confidence": 0.95, "timestamp": "...", "image_id": "...",
"ml": { "label": "ok", "confidence": 0.98 }, // 전용 ML 교차검증
"agreement": true, // VLM·ML 일치 여부
"requires_review": false // 불일치/저신뢰 → 사람·오라클 검토(③→④)
}
VLM verdict 가 여전히 주 판정이고 ML 은 교차검증이다. ML 호출 실패는 판정을 막지 않는다(degrade — VLM 단독 응답).
인증 모델
- 운영/읽기 무인증:
GET /api/u-vision/scenarios, 결과·기준이미지 조회·서빙,POST /api/u-vision/inspect·POST /api/u-vision/v2/inspect. - 관리/쓰기는 PIN: 시나리오 생성·수정·삭제, 기준이미지 업로드·삭제는 헤더
X-Admin-Pin필요. UVision:AdminPin미설정 → 관리 엔드포인트는 503{setup_required}(운영은 정상)이고, 데이터 디렉토리의 일회용admin-setup-token.txt로POST /admin/setup에서 첫 PIN 을 정한다(PBKDF2 해시 저장). 설정값 PIN 이 있으면 이 경로는 꺼진다. 계정·세션·역할 시스템은 의도적으로 없다.
관리자 PIN 최초 설정 (first-run bootstrap)
계정 시스템이 없으므로 신규 설치가 서버 파일 수정·재시작 없이 UI 만으로 첫 PIN 을 정할 수 있게 한다.
우선순위: 명시적 config PIN(UVision:AdminPin/ADMIN_PIN) > 파일 부트스트랩({DataPath}/admin-pin.json).
- config PIN 설정 시: 부트스트랩 완전 비활성 — setup-token 미생성,
POST /admin/setup은 410, 파일 미조회. 기존 동작과 byte-identical. - config PIN 미설정 시: 부팅 시 일회용 setup-token 을
{DataPath}/admin-setup-token.txt에 생성하고 경로를 로그에 남긴다. 서버 머신 파일시스템 접근 = 신뢰 루트(네트워크상 선점 탈취 방지). 관리 콘솔이 PIN 미설정을 감지하면 최초 설정 화면을 띄운다 — 토큰과 새 PIN 을 제출하면 PBKDF2 해시를admin-pin.json에 원자적 기록하고 토큰을 삭제한다. 이후 재시작 없이 즉시 관리 기능 사용 가능. - PIN 분실 복구: 서버에서
{DataPath}/admin-pin.json삭제 → 재시작 → 재부트스트랩(새 setup-token 생성).
API 표면 ({ApiBasePath} 하위, 기본 /api/u-vision)
전체 계약(요청 · 응답 · 오류)의 정본은 OpenAPI 문서다 — 리포의 server/openapi/u-vision.json, 호스트가 AddOpenApi() 를 부르면 같은 문서가 U-Vision 태그로 나온다. 아래는 지도다.
| 메서드 | 경로 | 인증 |
|---|---|---|
| GET | /health |
- |
| GET | /admin/status (PIN 설정 상태) |
- |
| POST | /admin/setup (최초 PIN 설정) |
setup-token |
| POST | /inspect (multipart) · /v2/inspect (결과 v2 — 오투입 확인은 expected_part · observed_code). 헤더 Idempotency-Key 를 주면 같은 키의 재전송은 첫 응답을 그대로 받는다 |
- |
| GET | /results, /results/dates, /results/image |
- |
| PUT/DELETE/GET | /results/label, /results/labels (사람 라벨) · /results/audit-sample, /results/audit (블라인드 감사) |
- |
| GET | /scenarios, /scenarios/{id} · /scenarios/{id}/references... · /scenarios/{id}/authority · /scenarios/{id}/models |
- |
| POST/PUT/DELETE | /scenarios... · references · authority promote/demote · models 등록 · promote · rollback |
PIN |
| GET | /datasets, /datasets/{exportId}/manifest |
- |
| POST | /datasets/export (라벨→ML 학습입력 스냅샷) |
PIN |
| GET | /metrics (관측 메트릭 집계) · /events/status (웹훅 outbox 상태) |
- |
JSON wire 는 snake_case(image_id, scenario_id, ng_label), verdict 는 "OK"/"NG". 이 계약은 DTO [JsonPropertyName]이 보유하므로 호스트의 전역 JSON 정책과 무관(호스트 JSON casing 을 오염시키지 않는다).
미들웨어 배치
UseUVision()은 {BasePath}/**(GET/HEAD)와 {ApiBasePath}/**만 처리하고 나머지는 통과시킨다(/u-vision-admin 같은 형제 경로도 삼키지 않음). 호스트의 catch-all/SPA fallback 보다 앞에 두면 안전하다. CORS 는 호스트 책임 — PWA 를 같은 호스트에서 서빙하면 불필요하다.
⚠️ 제약: BasePath 는 클라이언트 빌드와 결합
BasePath 기본값 /u-vision 은 임베드된 PWA 빌드의 base 와 일치한다. 다른 경로로 마운트하려면 클라이언트를 그 경로의 VITE_BASE 로 재빌드해야 한다 — service worker scope·manifest·번들 에셋이 빌드타임 base 를 baked 하기 때문이다(미들웨어는 index.html 만 best-effort 치환). 기본 /u-vision 사용을 권장한다.
스토리지
순수 파일시스템(DB 없음). {ContentRoot}/{UVision:Storage:DataPath}/{scenario}/... 아래에 시나리오 정의·기준 이미지·캡처·판정 결과가 누적된다. 단일 조직 셀프호스트의 진실의 원천.
라이선스
0.4.0 부터 AGPL-3.0-or-later(0.3.0 이하 게시본은 MIT 그대로). 이 패키지를 앱 안에 넣으면 그 앱 전체가 AGPL 조건을 따라야 한다 — AGPL 로 공개할 수 없는 앱은 엔진을 별도 서버로 띄워 HTTP 로 연동하거나 상용 라이선스를 받는다.
소스·이슈: https://github.com/iyulab/U-Vision · © 2026 iyulab.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- HoneAI.Core (>= 0.10.0)
- IronHive.Abstractions (>= 0.69.0)
- IronHive.Core (>= 0.69.0)
- IronHive.Providers.GoogleAI (>= 0.69.0)
- IronHive.Providers.OpenAI (>= 0.69.0)
- IronHive.Providers.OpenAI.Compatible (>= 0.69.0)
- SkiaSharp (>= 4.153.1)
- SkiaSharp.NativeAssets.Linux.NoDependencies (>= 4.153.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.