Simple URLSession не работает в Swift, что я делаю неправильно?

Я хочу создать простой CLI в Swift для работы с моделями OpenAI GPT. Я пробовал разные подходы. Приведенный ниже код является самой простой версией и все еще не работает - я считаю, что закрытие не вызывается.

let endpoint = "https://api.openai.com/v1/completions"
let headers = [
    "Content-Type": "application/json",
    "Authorization": "Bearer \(oai_key)"
]

let prompt = "Create a simple function in Swift that adds two numbers."

let parameters = [
    "model": "text-curie-001",
    "prompt": prompt,
    "max_tokens": 500,
    "temperature": 0.5,
    "n": 1
] as [String: Any]

let urlObject = URL(string: endpoint)!

var request = URLRequest(url: urlObject)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers

do {
    request.httpBody = try JSONSerialization.data(withJSONObject: parameters)
} catch {
    print(error)
}

func processResponseData(_ data: Data) {
    print(String(data: data, encoding: .utf8) ?? "")
}

let newTask = URLSession.shared.dataTask(with: request) {
    data, response, error in
    print("Completion handler closure called")
    
    if (error != nil) {
        print("This error occured: \(String(describing: error))")
    } else {
        print(String(describing: data))
        if let data = data {
            processResponseData(data)
        }
    }
}

print(newTask.state)
newTask.resume()
print(newTask.state)
print(newTask.response)
print(newTask.error)
print(newTask.progress)

Вывод в консоли от выполнения этого:

NSURLSessionTaskState
NSURLSessionTaskState
nil
nil
<NSProgress: 0x600002c08b80> : Parent: 0x0 (portion: 0) / Fraction completed: 0.0000 / Completed: 0 of 100  
  <NSProgress: 0x600002c08d80> : Parent: 0x600002c08b80 (portion: 95) / Fraction completed: 0.0000 / Completed: 0 of 100  
  <NSProgress: 0x600002c08d00> : Parent: 0x600002c08b80 (portion: 5) / Fraction completed: 0.0000 / Completed: 0 of 100  
Program ended with exit code: 0

Заранее спасибо!

Ваша программа завершается до завершения сетевой задачи?

Geoff Hackworth 03.04.2023 12:08

В CLI вам нужен runloop для выполнения асинхронного кода. И ваши строки печати после resume в любом случае бессмысленны, потому что, как уже упоминалось, задача выполняется асинхронно. Swift предоставляет async аналог main, который обрабатывает цикл выполнения от вашего имени.

vadian 03.04.2023 12:12
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
64
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Спасибо @vadian — runloop было отличным решением.

В итоге я почистил код после того, как runloop заработал, а затем обнаружил, что semaphore хорошо работает для моей реализации.

Вот код, на который я попал:

let openAI = OpenAIAPI()

func getInput() -> String {
    print("User:")
    if let input = readLine() {
        return input
    }
    return ""
}

func handleCompletion(responseText: String?) {
    if let responseText = responseText {
        print("Response:\n\(responseText)")
    } else {
        print("Error: Unable to get response")
    }
}

while true {
    let prompt = getInput()
    
    if prompt.lowercased() == "finished" {
        break
    }
    
    let semaphore = DispatchSemaphore(value: 0)
    
    openAI.getCompletion(prompt: prompt) { responseText in
        handleCompletion(responseText: responseText)
        semaphore.signal()
    }
    semaphore.wait()
}

Обратите внимание, что я обернул предыдущий код, который я опубликовал, в класс OpenAIAPI и добавил к нему дополнительную функциональность.

Строго говоря, это не runloop, это хак. А цикл while и семафор — ужасная практика во времена Swift Concurrency.

vadian 07.04.2023 16:09

Другие вопросы по теме