PHP for Assistant. Send question, get answer. Runs from console or call from a browser

I manage to write a PHP script that accept a question and return answer.
It design to work with Assistants.
The idea is that you’ll create and tune assistant at the web interface - no code is needed. Than you can interact with it through this script. You can chat it from a browser.

Here we go:

<?PHP
/*
Wrote it: Tal Bahir.
This scrip runs from the console. 
It take 1 parameter.
Example: php chatgpt.php "What you want to know"
To use it, create an assistant with vectore store if needed.
Put the assistant ID in the proper vaiable below
*/
//Yes/No if called from a browser or console
$runAsScript=(PHP_SAPI==='cli');
$previousTime = microtime(true);
// Variable to count the step number
$stepCounter = 0;
//If true will echo steps time
$lShowStepsTime=false;
if ($runAsScript)
	{
	session_id('runQueryToAssistant');
	}
session_start();
//Set to 1 to create new thread every run. Otherwise - reuse existing thread.)
if (0)
	{
	session_destroy();
//	session_unset();
//	die();
	}
//If true you can attach vector store. No need if already have it in your own Assistant
$useVectorStors=false;
$returnHTML=true;
$cQuery=isset($argv[1])?$argv[1]:'Put a question here';
if ($runAsScript)
	{
	echo "\nAsking Chatboot: $cQuery\n";
	}
$OPENAI_API_KEY="Your API KEY HERE";
//Your already ready assistant ID:
$ASST_ID="Your assistant ID here";
$VS_ID="If you have vector stor, you can put here Optional";
$FILESRCH_ID="Your search file here. Optional";
$THRD_ID=(isset($_SESSION['THRD_ID'])?$_SESSION['THRD_ID']:'');
$MSG_ID="";
$RUN_ID="";
$headers = [
    "Authorization: Bearer $OPENAI_API_KEY",
    'Content-Type: application/json',
    'Accept: application/json',
	"OpenAI-Beta: assistants=v2"
];

//Check if thread exists:
$url="https://api.openai.com/v1/threads/$THRD_ID";
$thread=json_decode(get_content($url,$nTO=30,$post=1,false,'',$headers),true);
echoStep('Check if thread exists');
//Check for errors
check_error($thread);

//If thread not exists create new one.
if (!$THRD_ID||!$thread['id'])
	{//Create new thread
	$url="https://api.openai.com/v1/threads";
	//With Vector Store
	if ($useVectorStors)// for thread without vector store
		{//Thread WITH vectore store
		$thread=json_decode(get_content($url,$nTO=30,$post=1,$userPass=false,'{
			"tool_resources": {
			  "file_search": {
				"vector_store_ids": ["'.$VS_ID.'"]
				}
			}
		}',$headers),true);
/*KEEP - In case wnat to attach vector_store:
		$thread=json_decode(get_content($url,$nTO=30,$post=1,$userPass=false,'{
			"tool_resources": {
			  "file_search": {
				"vector_stores": 
					[
					{"file_ids": ["'.$FILESRCH_ID.'"]}
					]
				}
			}
		}',$headers),true);
*/
		}
		else
		{//Thread without vectore store
		$thread=json_decode(get_content($url,$nTO=30,$post=1,$userPass=false,'',$headers),true);
		}
	echoStep('Thread created');
	//Check for errors
	check_error($thread);
	$THRD_ID=$thread['id'];
	$_SESSION['THRD_ID']=$THRD_ID;
	}
	else
	{//Thread exists
	$THRD_ID=$_SESSION['THRD_ID'];
	echoStep('Thread reused');
	}

//die(print_r($thread));

//Create a message:
$url="https://api.openai.com/v1/threads/$THRD_ID/messages";
$payload = json_encode(
	[
	"role" => "user",
	"content" => $cQuery
	]);

$message=json_decode(get_content($url,$nTO=30,$post=1,$userPass=false,$payload,$headers),true);
echoStep('Message created');

//Check for errors
check_error($message);
$MSG_ID=$message['id'];

//Create run:
$url="https://api.openai.com/v1/threads/$THRD_ID/runs";
$run=json_decode(get_content($url,$nTO=30,$post=1,$userPass=false,'{
	"assistant_id": "'.$ASST_ID.'",
	"tool_choice":"required",
	"tools":
		[
		{"type": "file_search"}
		]
	}',$headers),true);
echoStep('Run created');

/*
$run=json_decode(get_content($url,$nTO=30,$post=1,$userPass=false,'{
	"assistant_id": "'.$ASST_ID.'"
	}',$headers),true);
*/
	
	//Check for errors
check_error($run);
$RUN_ID=$run['id'];

//Wait for the run's completion
$url="https://api.openai.com/v1/threads/$THRD_ID/runs/$RUN_ID";
$nLoops=10;
while ($nLoops>0)
	{//Wait for completion
	sleep(4);
	$status=json_decode(get_content($url,$nTO=30,$post=0,$userPass=false,'',$headers,$writeCallback),true);
	check_error($status);
	echoStep("Run loop# $nLoops . Status: $status[status]");
	if ($status['status']=='completed')
		{
		break;
		}
	$nLoops--;
	}
if ($nLoops==0)
	{
	die(json_encode(["error"=>"Timeout waiting for the run completion"]));
	}
echoStep('Run completed');

//Get messages:
$url="https://api.openai.com/v1/threads/$THRD_ID/messages";
$output=get_content($url,$nTO=30,$post=0,$userPass=false,'',$headers);
$messages=json_decode($output,true);
//Check for errors
check_error($message);
echoStep('Message retrieved');

//Loop thrugh the messages to find the last run's answer	
$lMsgFound=false;
foreach($messages as $msgs)
	{
	if (is_array($msgs))
		{
		foreach($msgs as $msg)
			{
			if ($msg['run_id']==$RUN_ID)
				{
				$lMsgFound=true;
				break;
				}
			}
		}
	if ($lMsgFound)
		{
		break;
		}
	}
if ($lMsgFound)
	{
	/*Create embedding*/
	echoStep('Message found and ready');
	$headers=
		[
		"Authorization: Bearer $OPENAI_API_KEY",
		"Content-Type: application/json"
		];

	$data=json_encode(
		[
		"input" => $cQuery,  // Your question
		"model" => "text-embedding-ada-002"  // Example embedding model
		]);
	$response=json_decode(get_content("https://api.openai.com/v1/embeddings",30,0,false,$data,$headers),true);
	check_error($response);
	$embeddingString=json_encode($response['data'][0]['embedding']);
	/*Create embedding end*/
	$cMsg=$msg['content'][0]['text']['value'];
	$cMsg=json_encode(["results"=>($returnHTML?convertToHTML($cMsg):$cMsg),"error"=>"","embedding"=>$embeddingString]);
	}
	else
	{
	echoStep('Message NOT found');
	$cMsg=json_encode(["error"=>"Could not find the answer for the last question","results"=>"","embedding"=>""]);
	}

echoStep('Request completed');
if ($runAsScript)
	{//Show from script
	$cMsg=json_decode($cMsg,true);
	die("\nChatboot: $cMsg[results]\n"."\nIt has a ".strlen($cMsg['embedding'])." characters in embedding code\n");
	}
	else
	{//Send json string to browser:
	die("$cMsg\n");
	}
	
//Functions:
function check_error($response) 
	{
	if (isset($response['error'])) 
		{
		die(json_encode(["error" => $response['error']['message']]));
		}
	}
function get_content($url,$nTO=30,$post=0,$userPass=false,$payLoad=false,$headers=false,$writeFunction=null)
	{//
	//$userPass example: -u 'SK165acdc14eb71bbc07fb8b66dDdD84a64ce:1jSDdDVmW9EDcKUNz7NpFWKm1QZwHC01s9h'
	//$payLoad example:['From' => '+122344444','To' => '+13101111111','Body'   => 'This is the body...']
	$url = str_replace(" ", "%20", $url);
	$ch = curl_init();
	curl_setopt($ch, CURLOPT_URL, $url);
	curl_setopt($ch, CURLOPT_HEADER, 0);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
	curl_setopt($ch, CURLOPT_TIMEOUT,$nTO);
	curl_setopt($ch, CURLOPT_POST,$post);
	if ($userPass)
		{
		curl_setopt($ch, CURLOPT_USERPWD,$userPass);
		}
	if ($headers)
		{
		curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
		}
	if ($payLoad)
		{
		curl_setopt($ch, CURLOPT_POSTFIELDS,$payLoad);
		}
	$string=curl_exec($ch);
	$getcode=curl_getinfo($ch,CURLINFO_HTTP_CODE);
	if (!$string)
		{
		$string='ERR: '.curl_errno($ch).'. Problem is: '.curl_strerror(curl_errno($ch)).".<br>Likley - target not found.<br>URL: $url";
		}
	if ($getcode!=200&&curl_errno($ch)!='0'&&$getcode!='0')
		{
		$string="ERR: Not found. $getcode<br>URL: $url";
		}
	curl_close($ch);
	return $string;
	}
function convertToHTML($response) 
	{
	// Replace newline characters with <br> for HTML line breaks
	$response = nl2br($response);

	// Convert dashes used for lists into HTML unordered list
	$response = preg_replace('/\n-\s(.+)/', '<li>$1</li>', $response);

	// Wrap list items in <ul> tags
	$response = preg_replace('/(<li>.*<\/li>)/s', '<ul>$1</ul>', $response);

	// Return the resulting HTML
	return $response;
	}
function echoStep($message='') 
	{
	global $previousTime,$stepCounter,$runAsScript,$lShowStepsTime;
	if (!$runAsScript||!$lShowStepsTime) {return;}
	$stepCounter++;
	$currentTime = microtime(true);
	$timeTaken = $currentTime - $previousTime;
	$previousTime = $currentTime;
	//Show elaps time
	//echo "$message. Step $stepCounter. Elaps time: " . number_format($elapsedTime, 2) . " seconds\n";
	//Show step time
	echo "Step $stepCounter: Step's time: " . number_format($timeTaken, 2) . " seconds. $message\n";
	}

?>
2 Likes