Week 1 – Introduction & How LLMs Work: Slides and video
Week 2 – Prompt Engineering: Slides and video
Week 3 – Retrieval-Augmented Generation: Slides and video
Week 4 – Fine Tuning & Agents: Slides and video
Week 5 – VUMC’s OpenAI in PHP: Slides and video
namespace OpenAIApplication;
# Basic implementation; need to make more sophisticated for each use case
class OpenAI {
const ENDPOINT = "https://vumc-openai-24.openai.azure.com/openai/deployments/";
const URL_TAIL = "/chat/completions?api-version=2024-12-01-preview"; // might need to update with a version
const NO_DATA = "No Data";
public function __construct() {
$this->apiKey = self::getAPIKey();
$this->setDeployment("gpt-4.1"); // coordinated with OpenAI Studio
$this->temperature = 0.7; // normally creative, between 0.0 (deterministic) - 2.0 (crazy)
}
# TODO Edit with location of credentials directory
public static function getAPIKey(): string {
$apiKey = "";
$credentialsDir = "/credentials"; // TODO
if (!$credentialsDir) {
die("Invalid credentials");
}
include($credentialsDir."/APP_NAME_HERE/openai.php"); // TODO
if (!$apiKey) {
throw new \Exception("No API Key!");
}
return $apiKey;
}
# TODO Example prompt; need to change
public function getPubs(): string {
$prompt = "Without any commentary, provide the plain text for all publications, one per line, under the publications header. Do not summarize or reformat the Markdown in any way and include it verbatim. If no publications are provided, provide blank text. Check your work step by step.\n\n".$this->markdown;
$response = $this->submitPrompt($prompt);
return self::getMessage($response);
}
public function setDeployment(string $deploymentId): void {
$this->deploymentId = $deploymentId;
}
private static function getMessage(array $response, int $index = 0): string {
return $response["choices"][$index]["message"]["content"] ?? $response["error"]["message"] ?? self::NO_DATA;
}
private function getURL(): string {
return self::ENDPOINT.$this->deploymentId.self::URL_TAIL;
}
private function submitPrompt(string $message, bool $retryOnFailure = TRUE): array {
$postdata = [
"messages" => [
[
"role" => "system", // or "user"
"content" => $message
]
], // can have multiple messages, especially one from "system" (context) and the other from "user"
"temperature" => $this->temperature,
"seed" => self::getSeed(),
];
$opts = [
CURLOPT_HTTPHEADER => [
"api-key: ".$this->apiKey,
],
];
list($resp, $json) = self::downloadURLWithPOST($this->getURL(), $postdata, $opts);
$response = json_decode($json, TRUE);
if ($resp == 200) {
return $response;
} else if (isset($response['error']['message'])) {
if (preg_match("/Please retry after (\d+) seconds/i", $response['error']['message'], $matches)) {
$sleepSecs = ((int) $matches[1]) + 1;
} else {
$sleepSecs = 30;
}
if ($retryOnFailure) {
sleep($sleepSecs);
return $this->submitPrompt($message, FALSE);
} else {
throw new \Exception("ERROR: ".$response['error']['message']);
}
} else {
throw new \Exception("ERROR: $json");
}
}
# Can set to fixed integer if desiring more determinism
private static function getSeed(): int {
return rand(0, 1000000);
}
private static function getDefaultCURLOpts(): array {
return [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_VERBOSE => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_AUTOREFERER => true,
CURLOPT_MAXREDIRS => 10,
CURLOPT_FRESH_CONNECT => 1,
CURLOPT_TIMEOUT => 300, // some OpenAI parsing can take a long time
CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_SSL_VERIFYPEER => TRUE,
];
}
public static function downloadURLWithPOST(string $url, array $postdata = [], array $addlOpts = []): array {
if (!$url) {
throw new \Exception("Invalid URL!");
}
$defaultOpts = self::getDefaultCURLOpts();
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
foreach ($defaultOpts as $opt => $value) {
if (!isset($addlOpts[$opt])) {
curl_setopt($ch, $opt, $value);
}
}
foreach ($addlOpts as $opt => $value) {
if ($opt != CURLOPT_HTTPHEADER) {
curl_setopt($ch, $opt, $value);
}
}
if (!empty($postdata)) {
if (is_string($postdata)) {
$json = $postdata;
} else {
$json = json_encode($postdata);
}
$json = Sanitizer::sanitizeJSON($json);
if (!$json) {
throw new \Exception("Invalid POST parameters!");
}
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
curl_setopt($ch, CURLOPT_HTTPHEADER, array_merge([
'Content-Type: application/json',
], $addlOpts[CURLOPT_HTTPHEADER] ?? []));
}
$data = (string) curl_exec($ch);
$resp = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if(curl_errno($ch)){
throw new \Exception("Error number ".curl_errno($ch)." cURL Error: ".curl_error($ch));
}
curl_close($ch);
return [$resp, $data];
}
protected $apiKey;
protected $deploymentId;
protected $temperature;
}
Assistant Professor
Assistant Professor
Assistant Professor
Assistant Professor