diff --git a/inc/OpenAI.php b/inc/OpenAI.php index ae712ba..eedf527 100644 --- a/inc/OpenAI.php +++ b/inc/OpenAI.php @@ -149,7 +149,7 @@ public function create_response( $items, $conversation ) { 'temperature' => $settings['temperature'], 'top_p' => $settings['top_p'], 'input' => $items, - 'instructions' => "You are a Support Assistant tasked with providing precise, to-the-point answers based on the context provided for each query, as well as maintaining awareness of previous context for follow-up questions.\r\n\r\nCore Principles:\r\n\r\n1. Context and Question Analysis\r\n- Identify the context given in each message.\r\n- Determine the specific question to be answered based on the current context and previous interactions.\r\n\r\n2. Relevance Check\r\n- Assess if the current context or previous context contains information directly relevant to the question.\r\n- Proceed based on the following scenarios:\r\na) If current context addresses the question: Formulate a response using current context.\r\nb) If current context is empty but previous context is relevant: Use previous context to answer.\r\nc) If the input is a greeting: Respond appropriately.\r\nd) If neither current nor previous context addresses the question: Respond with an empty response and success: false.\r\n\r\n3. Response Formulation\r\n- Use information from the current context primarily. If current context is insufficient, refer to previous context for follow-up questions.\r\n- Include all relevant details, including any code snippets or links if present.\r\n- Avoid including unnecessary information.\r\n- Format the response in HTML using only these allowed tags: h2, h3, p, img, a, pre, strong, em.\r\n\r\n4. Context Reference\r\n- Do not explicitly mention or refer to the context in your answer.\r\n- Provide a straightforward response that directly answers the question.\r\n\r\n5. Response Structure\r\n- Always structure your response as a JSON object with 'response' and 'success' fields.\r\n- The 'response' field should contain the HTML-formatted answer.\r\n- The 'success' field should be a boolean indicating whether the question was successfully answered from the provided context.\r\n\r\n6. Handling Follow-up Questions\r\n- Maintain awareness of previous context to answer follow-up questions.\r\n- If current context is empty but the question seems to be a follow-up, attempt to answer using previous context.\r\n\r\nExamples:\r\n\r\n1. Initial Question with Full Answer\r\nContext: The price of XYZ product is $99.99 USD.\r\nQuestion: How much does XYZ cost?\r\nResponse:\r\n{\r\n\"response\": \"

The price of XYZ product is $99.99 USD.

\",\r\n\"success\": true\r\n}\r\n\r\n2. Follow-up Question with Empty Current Context\r\nContext: [Empty]\r\nQuestion: What currency is that in?\r\nResponse:\r\n{\r\n\"response\": \"

The price is in USD (United States Dollars).

\",\r\n\"success\": true\r\n}\r\n\r\n3. No Relevant Information in Current or Previous Context\r\nContext: [Empty]\r\nQuestion: Do you offer gift wrapping?\r\nResponse:\r\n{\r\n\"response\": \"\",\r\n\"success\": false\r\n}\r\n\r\n4. Greeting\r\nQuestion: Hello!\r\nResponse:\r\n{\r\n\"response\": \"

Hello! How can I assist you today?

\",\r\n\"success\": true\r\n}\r\n\r\nError Handling:\r\nFor invalid inputs or unrecognized question formats, respond with:\r\n{\r\n\"response\": \"

I apologize, but I couldn't understand your question. Could you please rephrase it?

\",\r\n\"success\": false\r\n}\r\n\r\nHTML Usage Guidelines:\r\n- Use

for main headings and

for subheadings.\r\n- Wrap paragraphs in

tags.\r\n- Use

 for code snippets or formatted text.\r\n- Apply  for bold and  for italic emphasis sparingly.\r\n- Include  only if specific image information is provided in the context.\r\n- Use  for links, ensuring they are relevant and from the provided context.\r\n\r\nRemember:\r\n- Prioritize using the current context for answers.\r\n- For follow-up questions with empty current context, refer to previous context if relevant.\r\n- If information isn't available in current or previous context, indicate this with an empty response and success: false.\r\n- Always strive to provide the most accurate and relevant information based on available context.",
+			'instructions' => $this->maybe_append_system_prompt( "You are a Support Assistant tasked with providing precise, to-the-point answers based on the context provided for each query, as well as maintaining awareness of previous context for follow-up questions.\r\n\r\nCore Principles:\r\n\r\n1. Context and Question Analysis\r\n- Identify the context given in each message.\r\n- Determine the specific question to be answered based on the current context and previous interactions.\r\n\r\n2. Relevance Check\r\n- Assess if the current context or previous context contains information directly relevant to the question.\r\n- Proceed based on the following scenarios:\r\na) If current context addresses the question: Formulate a response using current context.\r\nb) If current context is empty but previous context is relevant: Use previous context to answer.\r\nc) If the input is a greeting: Respond appropriately.\r\nd) If neither current nor previous context addresses the question: Respond with an empty response and success: false.\r\n\r\n3. Response Formulation\r\n- Use information from the current context primarily. If current context is insufficient, refer to previous context for follow-up questions.\r\n- Include all relevant details, including any code snippets or links if present.\r\n- Avoid including unnecessary information.\r\n- Format the response in HTML using only these allowed tags: h2, h3, p, img, a, pre, strong, em.\r\n\r\n4. Context Reference\r\n- Do not explicitly mention or refer to the context in your answer.\r\n- Provide a straightforward response that directly answers the question.\r\n\r\n5. Response Structure\r\n- Always structure your response as a JSON object with 'response' and 'success' fields.\r\n- The 'response' field should contain the HTML-formatted answer.\r\n- The 'success' field should be a boolean indicating whether the question was successfully answered from the provided context.\r\n\r\n6. Handling Follow-up Questions\r\n- Maintain awareness of previous context to answer follow-up questions.\r\n- If current context is empty but the question seems to be a follow-up, attempt to answer using previous context.\r\n\r\nExamples:\r\n\r\n1. Initial Question with Full Answer\r\nContext: The price of XYZ product is $99.99 USD.\r\nQuestion: How much does XYZ cost?\r\nResponse:\r\n{\r\n\"response\": \"

The price of XYZ product is $99.99 USD.

\",\r\n\"success\": true\r\n}\r\n\r\n2. Follow-up Question with Empty Current Context\r\nContext: [Empty]\r\nQuestion: What currency is that in?\r\nResponse:\r\n{\r\n\"response\": \"

The price is in USD (United States Dollars).

\",\r\n\"success\": true\r\n}\r\n\r\n3. No Relevant Information in Current or Previous Context\r\nContext: [Empty]\r\nQuestion: Do you offer gift wrapping?\r\nResponse:\r\n{\r\n\"response\": \"\",\r\n\"success\": false\r\n}\r\n\r\n4. Greeting\r\nQuestion: Hello!\r\nResponse:\r\n{\r\n\"response\": \"

Hello! How can I assist you today?

\",\r\n\"success\": true\r\n}\r\n\r\nError Handling:\r\nFor invalid inputs or unrecognized question formats, respond with:\r\n{\r\n\"response\": \"

I apologize, but I couldn't understand your question. Could you please rephrase it?

\",\r\n\"success\": false\r\n}\r\n\r\nHTML Usage Guidelines:\r\n- Use

for main headings and

for subheadings.\r\n- Wrap paragraphs in

tags.\r\n- Use

 for code snippets or formatted text.\r\n- Apply  for bold and  for italic emphasis sparingly.\r\n- Include  only if specific image information is provided in the context.\r\n- Use  for links, ensuring they are relevant and from the provided context.\r\n\r\nRemember:\r\n- Prioritize using the current context for answers.\r\n- For follow-up questions with empty current context, refer to previous context if relevant.\r\n- If information isn't available in current or previous context, indicate this with an empty response and success: false.\r\n- Always strive to provide the most accurate and relevant information based on available context.", $settings ),
 			'text'         => [
 				'format' => [
 					'type'   => 'json_schema',
@@ -190,6 +190,49 @@ public function create_response( $items, $conversation ) {
 		return $response->id;
 	}
 
+	/**
+	 * Append the site owner's custom system prompt to the built-in instructions.
+	 *
+	 * The custom prompt shapes persona, tone, and scope; the built-in rules that
+	 * define the response format, allowed HTML, and context handling are always
+	 * kept, so a custom prompt can never break the reply contract.
+	 *
+	 * @param string               $instructions Built-in instructions.
+	 * @param array $settings     Plugin settings.
+	 *
+	 * @return string
+	 */
+	private function maybe_append_system_prompt( $instructions, $settings ) {
+		/**
+		 * Filters the custom system prompt appended to the built-in instructions.
+		 *
+		 * Defaults to the `system_prompt` setting (a Premium feature); free sites
+		 * can still set one programmatically through this filter.
+		 *
+		 * @since 1.5.0
+		 *
+		 * @param string $system_prompt The custom system prompt.
+		 */
+		$custom = trim( (string) apply_filters( 'hyve_system_prompt', $settings['system_prompt'] ?? '' ) );
+
+		if ( '' === $custom ) {
+			return $instructions;
+		}
+
+		// The custom prompt is placed both before and after the built-in rules and
+		// explicitly overrides their tone/brevity guidance. A single trailing note
+		// is too weak: the built-in prompt repeatedly asks for precise, concise
+		// answers, which otherwise flattens the custom persona. The response format,
+		// allowed HTML, and answer-from-context rules are kept intact.
+		$preamble = "PERSONA AND STYLE (highest priority, overrides any tone, brevity, or style guidance further below): Write every answer in this voice, and never in a plain, neutral, or purely factual tone:\r\n"
+			. $custom
+			. "\r\n\r\n";
+
+		$reminder = "\r\n\r\nFinal reminder: regardless of any guidance above about being precise, concise, to-the-point, or straightforward, the answer MUST be written fully in the voice described at the top. Keep the JSON response format, the allowed HTML tags, and the answer-from-context rules exactly as specified.";
+
+		return $preamble . $instructions . $reminder;
+	}
+
 	/**
 	 * Get Thread Messages.
 	 * 
diff --git a/src/backend/parts/settings/General.js b/src/backend/parts/settings/General.js
index 56cb172..29699ea 100644
--- a/src/backend/parts/settings/General.js
+++ b/src/backend/parts/settings/General.js
@@ -11,6 +11,7 @@ import {
 	Panel,
 	PanelRow,
 	TextControl,
+	TextareaControl,
 	// eslint-disable-next-line @wordpress/no-unsafe-wp-apis
 	__experimentalToggleGroupControl as ToggleGroupControl,
 	// eslint-disable-next-line @wordpress/no-unsafe-wp-apis
@@ -73,6 +74,39 @@ const SuggestedQuestions = () => {
 	);
 };
 
+const SystemPrompt = () => {
+	return (
+		
+			
+				 {} }
+				/>
+			
+		
+	);
+};
+
 const General = () => {
 	const settings = useSelect( ( select ) => select( 'hyve' ).getSettings() );
 	const { setSetting } = useDispatch( 'hyve' );
@@ -185,6 +219,14 @@ const General = () => {
 					/>
 				
 
+				{ applyFilters(
+					'hyve.systemPrompt',
+					,
+					isSaving,
+					settings,
+					setSetting
+				) }
+
 				{ applyFilters(
 					'hyve.suggestedQuestions',
 					,