Files
hyungi_document_server/Tests/AITests/FixtureSupport.swift
T
hyungi 5383a93f98 feat(ai-fabric): S2 LLM 패브릭 4 provider 결선 + 컴포지션 루트
risk-first 채움(RemoteDS→LocalMLX→OnDevice→Specialized) + makeDefaultRouter 컴포지션 루트.
동결 인터페이스(AIProvider/AIRouter/MockAIProvider) 무변경. SPM AIFabric 단독 빌드·테스트(46 PASS).

- RemoteDS: DSAskClient seam + AskResponse(ask.json) 매핑 + backend exhaustive switch(qwen/cloud TODO)
- LocalMLX: GET /v1/models probe + OpenAI /v1/chat/completions system/user call-shape + non-200 backendError
- OnDevice: FoundationModels 라이브(M5 Max) availability + respond() + GenerationError 9-case 매핑 + stateless/prewarm
- Specialized: scaffold-only(명시 unavailable, vision 폴백 가시화), cloud='claude-cloud' 503
- config 단일소스(env override) + 타임아웃/취소(URLSession 자동 honor, OnDevice 협조적)

실측 동결(S2-3a, M5 Max): availability=available · 취소=COOPERATIVE(~33ms) · 오버플로=exceededContextWindowSize
  · GenerationError 9-case(refusal·concurrentRequests 추가 발견, plan 정정).
한계: LocalMLX fixture=PROVISIONAL_SYNTHETIC(맥미니 offline → 라이브 재캡처 S2-Ff 대기).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 17:20:10 +09:00

27 lines
1.2 KiB
Swift

// FixtureSupport.swift canonical fixture (contract/fixtures/ #filePath ).
//
// repo `contract/fixtures/` (S1 ask.json + S2
// foundationmodels-respond / llm-router-chat).
// #filePath repo canonical .
import Foundation
enum Fixture {
/// repo (.../ds-app-s2) <root>/Tests/AITests/FixtureSupport.swift.
static let repoRoot: URL = URL(fileURLWithPath: #filePath)
.deletingLastPathComponent() // Tests/AITests
.deletingLastPathComponent() // Tests
.deletingLastPathComponent() // <root>
static func url(_ name: String) -> URL {
repoRoot.appendingPathComponent("contract/fixtures").appendingPathComponent(name)
}
static func data(_ name: String) throws -> Data {
try Data(contentsOf: url(name))
}
static func decode<T: Decodable>(_ type: T.Type, from name: String, using decoder: JSONDecoder = JSONDecoder()) throws -> T {
try decoder.decode(type, from: data(name))
}
}