Files
hyungi_document_server/Sources/DSKit/Models/Search.swift
T
Hyungi 0becf7829e feat(s3): SwiftPM scaffold + DSKit data layer + 14-fixture acceptance
- Package.swift: AI (S2-owned) + DSKit (models/client/fixtures) + DSKitTests, tools 6.2, .swiftLanguageMode(.v6), .macOS(.v26)
- JSONValue (Sendable AnyCodable), DSDate (value-type ISO8601FormatStyle cascade, date-only UTC), explicit-CodingKeys decoder
- Models: Auth/Document(+Detail flat-compose, MD-first)/Catalog/Search+Ask/Memo/Digest; non-optional limited to id/file_type/created+updated_at/total
- DSClient protocol + FixtureDSClient (Bundle.module, zero backend) + DSError + DSConfig + DownloadURL (?token= query)
- Tests: 14-fixture contract acceptance (value asserts) + JSONValue number trap + Ask round-trip + AI router fallback/explicit-unavailable

swift build + swift test green (19 tests). Sources/AI untouched.

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

104 lines
3.3 KiB
Swift

import Foundation
public struct SearchResult: Codable, Sendable, Identifiable {
public let id: Int // doc_id
public let title: String?
public let aiDomain: String?
public let aiSummary: String?
public let fileFormat: String?
public let score: Double?
public let snippet: String?
public let matchReason: String?
public let chunkId: Int?
public let chunkIndex: Int?
public let sectionTitle: String?
public let rerankScore: Double?
public let freshnessDebug: JSONValue?
enum CodingKeys: String, CodingKey {
case id, title, score, snippet
case aiDomain = "ai_domain"
case aiSummary = "ai_summary"
case fileFormat = "file_format"
case matchReason = "match_reason"
case chunkId = "chunk_id"
case chunkIndex = "chunk_index"
case sectionTitle = "section_title"
case rerankScore = "rerank_score"
case freshnessDebug = "freshness_debug"
}
}
public struct SearchResponse: Codable, Sendable {
public let results: [SearchResult]
public let total: Int
public let query: String
public let mode: String
public let debug: JSONValue?
}
/// Ask-response citation. DISTINCT from S2's `AICitation` (Sources/AI) different field sets;
/// RemoteDSProvider maps this -> AICitation itself. Do not reuse the S2 type here.
public struct Citation: Codable, Sendable, Identifiable {
public let n: Int
public let chunkId: Int?
public let docId: Int
public let title: String?
public let sectionTitle: String?
public let spanText: String
public let fullSnippet: String
public let relevance: Double
public let rerankScore: Double
public var id: Int { n }
enum CodingKeys: String, CodingKey {
case n, title, relevance
case chunkId = "chunk_id"
case docId = "doc_id"
case sectionTitle = "section_title"
case spanText = "span_text"
case fullSnippet = "full_snippet"
case rerankScore = "rerank_score"
}
}
public struct ConfirmedItem: Codable, Sendable {
public let aspect: String
public let text: String
public let citations: [Int]
}
public struct AskResponse: Codable, Sendable {
public let results: [SearchResult]
public let aiAnswer: String?
public let citations: [Citation]
public let synthesisStatus: String
public let synthesisMs: Double
public let confidence: String?
public let refused: Bool
public let noResultsReason: String?
public let query: String
public let total: Int
public let completeness: String
public let coveredAspects: [String]?
public let missingAspects: [String]?
public let confirmedItems: [ConfirmedItem]?
public let backendRequested: String?
public let backendUsed: String?
public let debug: JSONValue?
enum CodingKeys: String, CodingKey {
case results, citations, confidence, refused, query, total, completeness, debug
case aiAnswer = "ai_answer"
case synthesisStatus = "synthesis_status"
case synthesisMs = "synthesis_ms"
case noResultsReason = "no_results_reason"
case coveredAspects = "covered_aspects"
case missingAspects = "missing_aspects"
case confirmedItems = "confirmed_items"
case backendRequested = "backend_requested"
case backendUsed = "backend_used"
}
}