swiftpython-requestsopenai-apiurlsession

Simple URLSession not functioning in swift, what am I doing wrong?


I'm looking to create a simple CLI in Swift for working with OpenAI's GPT models. I've tried various approaches. The code below is the simplest version and still not functional -- I believe the closure is not being called.

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)

The output in the console from executing this is:

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

Thanks in advance!


Solution

  • Thanks to @vadian -- a runloop was a great solution.

    I ended up cleaning up the code after the runloop got it working and then found a semaphore worked well for my implementation.

    Here's the code I landed on:

    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()
    }
    

    Note that I wrapped the previous code I posted in an OpenAIAPI class and added additional functionality to it.