From 17230b3910bb7a4f56a3acfc8199e862a61fb31e Mon Sep 17 00:00:00 2001
From: Siraj R Aizlewood <siraj@aurelio.ai>
Date: Mon, 6 May 2024 14:25:17 +0400
Subject: [PATCH] Revert "Different methods of obtaining arguments for OpenAI"

This reverts commit 47a0fa3d79f1b2b7fb7f19f44c97717210ca9b78.
---
 semantic_router/llms/openai.py | 191 ++-------------------------------
 1 file changed, 7 insertions(+), 184 deletions(-)

diff --git a/semantic_router/llms/openai.py b/semantic_router/llms/openai.py
index 6b1bebba..1e121a3e 100644
--- a/semantic_router/llms/openai.py
+++ b/semantic_router/llms/openai.py
@@ -2,6 +2,7 @@ import os
 from typing import List, Optional, Any, Callable, Dict
 
 import openai
+from openai._types import NotGiven
 
 from semantic_router.llms import BaseLLM
 from semantic_router.schema import Message
@@ -63,8 +64,8 @@ class OpenAILLM(BaseLLM):
             if function_schemas:
                 tools = function_schemas
             else:
-                tools = None
-        
+                tools = NotGiven
+
             completion = self.client.chat.completions.create(
                 model=self.name,
                 messages=[m.to_openai() for m in messages],
@@ -95,192 +96,15 @@ class OpenAILLM(BaseLLM):
         except Exception as e:
             logger.error(f"LLM error: {e}")
             raise Exception(f"LLM error: {e}") from e
-    
-        
-    def _extract_multiple_function_inputs(self, query: str, function_schemas: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
-
-        prompt = f"""
-You are an accurate and reliable computer program that only outputs valid JSON. 
-Your task is to:
-    1) Pick the most relevant Python function schema(s) from FUNCTION_SCHEMAS below, based on the input QUERY. If only one schema is provided, choose that. If multiple schemas are relevant, output a list of JSON objects for each.
-    2) Output JSON representing the input arguments of the chosen function schema(s), including the function name, with argument values determined by information in the QUERY.
-
-These are the Python functions' schema:
-
-### FUNCTION_SCHEMAS Start ###
-    {json.dumps([schema['function'] for schema in function_schemas], indent=4)}
-### FUNCTION_SCHEMAS End ###
-
-This is the input query.
-
-### QUERY Start ###
-    {query}
-### QUERY End ###
-
-The arguments that you need to provide values for, together with their datatypes, are stated in the "parameters" in the FUNCTION_SCHEMA.
-The values these arguments must take are made clear by the QUERY.
-Use the FUNCTION_SCHEMA "description" too, as this might provide helpful clues about the arguments and their values.
-Include the function name in your JSON output.
-Return only JSON, stating the function name and the argument names with their corresponding values.
-
-### FORMATTING_INSTRUCTIONS Start ###
-    Return a response in valid JSON format. Do not return any other explanation or text, just the JSON.
-    The JSON output should always be an array of JSON objects. If only one function is relevant, return an array with a single JSON object.
-    Each JSON object should include a key 'function_name' with the value being the name of the function.
-    Under the key 'arguments', include a nested JSON object where the keys are the names of the arguments and the values are the values those arguments should take.
-### FORMATTING_INSTRUCTIONS End ###
 
-### EXAMPLE Start ###
-    === EXAMPLE_INPUT_QUERY Start ===
-        "What is the temperature in Hawaii and New York right now in Celsius, and what is the humidity in Hawaii?"
-    === EXAMPLE_INPUT_QUERY End ===
-    === EXAMPLE_INPUT_SCHEMA Start ===
-        {{
-            "name": "get_temperature",
-            "description": "Useful to get the temperature in a specific location",
-            "parameters": {{
-                "type": "object",
-                "properties": {{
-                    "location": {{
-                        "type": "string",
-                        "description": "The location to get the temperature from."
-                    }},
-                    "degree": {{
-                        "type": "string",
-                        "description": "The degree type, e.g., Celsius or Fahrenheit."
-                    }}
-                }},
-                "required": ["location", "degree"]
-            }}
-        }}
-    === EXAMPLE_INPUT_SCHEMA End ===
-    === EXAMPLE_OUTPUT Start ===
-        [
-            {{
-                "function_name": "get_temperature",
-                "arguments": {{
-                    "location": "Hawaii",
-                    "degree": "Celsius"
-                }}
-            }},
-            {{
-                "function_name": "get_temperature",
-                "arguments": {{
-                    "location": "New York",
-                    "degree": "Celsius"
-                }}
-            }},
-            {{
-                "function_name": "get_humidity",
-                "arguments": {{
-                    "location": "Hawaii"
-                }}
-            }}
-        ]
-    === EXAMPLE_OUTPUT End ===
-### EXAMPLE End ###
-
-Note: I will tip $500 for an accurate JSON output. You will be penalized for an inaccurate JSON output.
-
-Provide JSON output now:
-"""
-        llm_input = [Message(role="user", content=prompt)]
-        output = self(llm_input)
-        if not output:
-            raise Exception("No output generated for extract function input")
-
-        output = output.replace("'", '"').strip().rstrip(",")
-        logger.info(f"LLM output: {output}")
-        function_inputs = json.loads(output)
-        if not isinstance(function_inputs, list): # Local LLMs return a single JSON object that isn't in an array sometimes.
-            function_inputs = [function_inputs]
-        logger.info(f"Function inputs: {function_inputs}")
-        if not self._is_valid_inputs(function_inputs, function_schemas):
-            raise ValueError("Invalid inputs")
-        return function_inputs
-    
-    def _extract_single_function_input(self, query: str, function_schemas: Dict[str, Any]) -> Dict[str, Any]:
+    def extract_function_inputs(
+        self, query: str, function_schemas: List[Dict[str, Any]]
+    ) -> Dict:
         messages = []
         system_prompt = "You are an intelligent AI. Given a command or request from the user, call the function to complete the request."
         messages.append(Message(role="system", content=system_prompt))
         messages.append(Message(role="user", content=query))
-        function_inputs = self(messages=messages, function_schemas=function_schemas)
-
-        if not self._is_valid_inputs(function_inputs, function_schemas):
-            raise ValueError("Invalid inputs")
-        return function_inputs
-
-    def extract_function_inputs(
-        self, query: str, function_schemas: List[Dict[str, Any]]
-    ) -> Dict:
-
-        if len(function_schemas) == 0:
-            raise ValueError("No function schemas provided")
-        elif len(function_schemas) == 1:
-            logger.info("Extracting single function input...")
-            return self._extract_single_function_input(query, function_schemas)
-        else:
-            logger.info("Extracting multiple function inputs...")
-            return self._extract_multiple_function_inputs(query, function_schemas)
-        
-    def _is_valid_inputs(
-        self, inputs: List[Dict[str, Any]], function_schemas: List[Dict[str, Any]]
-    ) -> bool:
-        """Determine if the functions chosen by the LLM exist within the function_schemas, 
-        and if the input arguments are valid for those functions."""
-        try:
-            for input_dict in inputs:
-                # Check if 'function_name' and 'arguments' keys exist in each input dictionary
-                if "function_name" not in input_dict or "arguments" not in input_dict:
-                    logger.error("Missing 'function_name' or 'arguments' in inputs")
-                    return False
-
-                function_name = input_dict["function_name"]
-                arguments = input_dict["arguments"]
-
-                # Find the matching function schema based on function_name
-                matching_schema = next((schema['function'] for schema in function_schemas if schema['function']['name'] == function_name), None)
-                if not matching_schema:
-                    logger.error(f"No matching function schema found for function name: {function_name}")
-                    return False
-
-                # Validate the inputs against the function schema
-                if not self._validate_single_function_inputs(arguments, matching_schema):
-                    logger.error(f"Validation failed for function name: {function_name}")
-                    return False
-
-            return True
-        except Exception as e:
-            logger.error(f"Input validation error: {str(e)}")
-            return False
-            
-    def _validate_single_function_inputs(self, inputs: Dict[str, Any], function_schema: Dict[str, Any]) -> bool:
-        """Validate the extracted inputs against the function schema"""
-        try:
-            # Access the parameters and their properties from the function schema directly
-            parameters = function_schema['parameters']['properties']
-            required_params = function_schema['parameters'].get('required', [])
-
-            # Check if all required parameters are present in the inputs
-            for param_name in required_params:
-                if param_name not in inputs:
-                    logger.error(f"Required input '{param_name}' missing from query")
-                    return False
-
-            # Check if the types of the inputs match the expected types (if type checking is needed)
-            for param_name, param_info in parameters.items():
-                if param_name in inputs:
-                    expected_type = param_info['type']
-                    # This is a simple type check, consider expanding it based on your needs
-                    if expected_type == 'string' and not isinstance(inputs[param_name], str):
-                        logger.error(f"Input type for '{param_name}' is not {expected_type}")
-                        return False
-
-            return True
-        except Exception as e:
-            logger.error(f"Single input validation error: {str(e)}")
-            return False
-
+        return self(messages=messages, function_schemas=function_schemas)
 
 def get_schemas_openai(items: List[Callable]) -> List[Dict[str, Any]]:
     schemas = []
@@ -330,4 +154,3 @@ def get_schemas_openai(items: List[Callable]) -> List[Dict[str, Any]]:
 
     return schemas
 
-
-- 
GitLab