5383a93f98
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>
27 lines
1.2 KiB
Swift
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))
|
|
}
|
|
}
|