4093b5a7f3
Tapping a verb now shows six example sentences beneath the conjugation table, one per core tense (Present, Preterite, Imperfect, Future, Present Subjunctive, Imperative). Each example renders the tense label, Spanish sentence, and English translation in the DeckStudyView style. Generation uses Foundation Models with a @Generable schema that pins each response to the requested tenseId and forces tú/nosotros subjects for imperatives. Results are cached as JSON in the Caches directory keyed by verb id (DictionaryService pattern); cache misses regenerate on demand. Devices without Apple Intelligence see an inline notice instead of the loading state. No network dependency. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
79 lines
3.3 KiB
Swift
79 lines
3.3 KiB
Swift
import Foundation
|
|
import FoundationModels
|
|
import SharedModels
|
|
|
|
/// Generates a set of example sentences for a single verb, one per core tense
|
|
/// (Issue #27). Mirrors the StoryGenerator pattern: @Generable response types,
|
|
/// a static availability flag, and a single generate(...) entry point.
|
|
@MainActor
|
|
struct VerbExampleGenerator {
|
|
|
|
// MARK: - Generable Types
|
|
|
|
@Generable
|
|
struct GeneratedExampleSet {
|
|
@Guide(
|
|
description: "Six example sentences, one per tense in the exact order requested. Each sentence must actually use the target verb conjugated in that tense.",
|
|
.count(6)
|
|
)
|
|
var examples: [GeneratedExample]
|
|
}
|
|
|
|
@Generable
|
|
struct GeneratedExample {
|
|
@Guide(description: "The tense id this sentence demonstrates. Must match one of the ids provided in the prompt exactly (e.g. ind_presente).")
|
|
var tenseId: String
|
|
|
|
@Guide(description: "A natural Spanish sentence, 6-14 words, that uses the target verb in the specified tense. For imperative tenses use tú or nosotros — never yo.")
|
|
var spanish: String
|
|
|
|
@Guide(description: "An accurate, idiomatic English translation of the Spanish sentence.")
|
|
var english: String
|
|
}
|
|
|
|
// MARK: - Generation
|
|
|
|
/// Generate one example per tense in `tenseIds`. Returns the examples in the
|
|
/// same order as `tenseIds`, filling in placeholders for any the model skipped.
|
|
static func generate(
|
|
verbInfinitive: String,
|
|
verbEnglish: String,
|
|
tenseIds: [String]
|
|
) async throws -> [VerbExample] {
|
|
let tenseList = tenseIds
|
|
.compactMap { id in TenseInfo.find(id).map { "\(id) (\($0.english))" } }
|
|
.joined(separator: ", ")
|
|
|
|
let session = LanguageModelSession(instructions: """
|
|
You are a Spanish language teacher writing short example sentences for a learner.
|
|
The learner is studying the verb "\(verbInfinitive)" (to \(verbEnglish)).
|
|
Write one sentence per requested tense. Each sentence must:
|
|
- Actually conjugate "\(verbInfinitive)" in that tense (not just mention it).
|
|
- Be 6-14 words, natural and everyday.
|
|
- Use vocabulary appropriate for intermediate learners.
|
|
- Vary subjects and contexts across the set; do not reuse the same subject twice.
|
|
For imperative tenses, address "tú" or "nosotros" — never "yo".
|
|
""")
|
|
|
|
let prompt = """
|
|
Write example sentences for "\(verbInfinitive)" in these tenses, in this order:
|
|
\(tenseList)
|
|
|
|
Return one GeneratedExample per tense with the matching tenseId, spanish, and english.
|
|
"""
|
|
|
|
let response = try await session.respond(to: prompt, generating: GeneratedExampleSet.self)
|
|
|
|
// Map by tenseId and return in the caller's requested order so the UI
|
|
// renders a predictable sequence even if the model shuffles its output.
|
|
let byTense = Dictionary(uniqueKeysWithValues: response.content.examples.map {
|
|
($0.tenseId, VerbExample(tenseId: $0.tenseId, spanish: $0.spanish, english: $0.english))
|
|
})
|
|
return tenseIds.compactMap { byTense[$0] }
|
|
}
|
|
|
|
static var isAvailable: Bool {
|
|
SystemLanguageModel.default.availability == .available
|
|
}
|
|
}
|