| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105 |
- import Foundation
- protocol DictionaryServicing {
- func lookUp(word: String) async throws -> DictionaryEntry
- }
- struct DictionaryService: DictionaryServicing {
- private let openAI: OpenAIChatServicing
- init(openAI: OpenAIChatServicing = OpenAIChatService()) {
- self.openAI = openAI
- }
- func lookUp(word: String) async throws -> DictionaryEntry {
- let trimmed = word.trimmingCharacters(in: .whitespacesAndNewlines)
- guard !trimmed.isEmpty else {
- throw DictionaryServiceError.emptyInput
- }
- do {
- let response: DictionaryAPIResponse = try await openAI.completeJSON(
- systemPrompt: """
- You are an English dictionary. Define the requested word. Respond with JSON only using this schema:
- {
- "word": "the word",
- "phonetic": "/phonetic transcription/ or null",
- "meanings": [
- {
- "partOfSpeech": "noun",
- "definitions": [
- {
- "text": "definition text",
- "example": "example sentence or null"
- }
- ],
- "synonyms": ["synonym"],
- "antonyms": ["antonym"]
- }
- ]
- }
- If the word is not a valid English word, return an empty meanings array.
- """,
- userPrompt: trimmed,
- as: DictionaryAPIResponse.self
- )
- guard !response.meanings.isEmpty else {
- throw DictionaryServiceError.wordNotFound
- }
- return DictionaryEntry(
- word: response.word,
- phonetic: response.phonetic,
- audioURL: nil,
- meanings: response.meanings.map { meaning in
- DictionaryMeaning(
- partOfSpeech: meaning.partOfSpeech,
- definitions: meaning.definitions.map {
- DictionaryDefinition(text: $0.text, example: $0.example)
- },
- synonyms: meaning.synonyms,
- antonyms: meaning.antonyms
- )
- }
- )
- } catch let error as OpenAIServiceError {
- throw DictionaryServiceError.fromOpenAI(error)
- } catch let error as DictionaryServiceError {
- throw error
- } catch {
- throw DictionaryServiceError.invalidResponse
- }
- }
- }
- private struct DictionaryAPIResponse: Decodable {
- struct Meaning: Decodable {
- struct Definition: Decodable {
- let text: String
- let example: String?
- }
- let partOfSpeech: String
- let definitions: [Definition]
- let synonyms: [String]
- let antonyms: [String]
- }
- let word: String
- let phonetic: String?
- let meanings: [Meaning]
- }
- private extension DictionaryServiceError {
- static func fromOpenAI(_ error: OpenAIServiceError) -> DictionaryServiceError {
- switch error {
- case .missingAPIKey:
- .missingAPIKey
- case .invalidResponse:
- .invalidResponse
- case .apiError(let message):
- .networkError(message)
- }
- }
- }
|