-1

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!

  • 1
    Is your program terminating before the network task has had a chance to finish? – Geoff Hackworth Apr 03 '23 at 10:08
  • 1
    In a CLI you need a `runloop` to execute asynchronous code. And your print lines after `resume` are pointless anyway because - as mentioned - the task runs **a**synchronously. Swift provides an `async` counterpart of `main` which handles the runloop on your behalf. – vadian Apr 03 '23 at 10:12

1 Answers1

-2

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.

  • Strictly spoken this is not a runloop, it's a hack. And a `while` loop and a Semaphore is a horrible practice in times of Swift Concurrency. – vadian Apr 07 '23 at 14:09