revert: Revert "feat: fix streaming put_inner_thoughts_in_kwargs" (#1912)

This commit is contained in:
Charles Packer 2024-10-19 23:48:27 -07:00 committed by GitHub
parent 9e83dbc27f
commit eb97f157ff
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 24 additions and 62 deletions

View File

@ -1,7 +1,6 @@
import copy import copy
import json import json
import warnings import warnings
from collections import OrderedDict
from typing import Any, List, Union from typing import Any, List, Union
import requests import requests
@ -11,30 +10,6 @@ from letta.schemas.openai.chat_completion_response import ChatCompletionResponse
from letta.utils import json_dumps, printd from letta.utils import json_dumps, printd
def convert_to_structured_output(openai_function: dict) -> dict:
"""Convert function call objects to structured output objects
See: https://platform.openai.com/docs/guides/structured-outputs/supported-schemas
"""
structured_output = {
"name": openai_function["name"],
"description": openai_function["description"],
"strict": True,
"parameters": {"type": "object", "properties": {}, "additionalProperties": False, "required": []},
}
for param, details in openai_function["parameters"]["properties"].items():
structured_output["parameters"]["properties"][param] = {"type": details["type"], "description": details["description"]}
if "enum" in details:
structured_output["parameters"]["properties"][param]["enum"] = details["enum"]
# Add all properties to required list
structured_output["parameters"]["required"] = list(structured_output["parameters"]["properties"].keys())
return structured_output
def make_post_request(url: str, headers: dict[str, str], data: dict[str, Any]) -> dict[str, Any]: def make_post_request(url: str, headers: dict[str, str], data: dict[str, Any]) -> dict[str, Any]:
printd(f"Sending request to {url}") printd(f"Sending request to {url}")
try: try:
@ -103,34 +78,33 @@ def add_inner_thoughts_to_functions(
inner_thoughts_key: str, inner_thoughts_key: str,
inner_thoughts_description: str, inner_thoughts_description: str,
inner_thoughts_required: bool = True, inner_thoughts_required: bool = True,
# inner_thoughts_to_front: bool = True, TODO support sorting somewhere, probably in the to_dict?
) -> List[dict]: ) -> List[dict]:
"""Add an inner_thoughts kwarg to every function in the provided list, ensuring it's the first parameter""" """Add an inner_thoughts kwarg to every function in the provided list"""
# return copies
new_functions = [] new_functions = []
# functions is a list of dicts in the OpenAI schema (https://platform.openai.com/docs/api-reference/chat/create)
for function_object in functions: for function_object in functions:
function_params = function_object["parameters"]["properties"]
required_params = list(function_object["parameters"]["required"])
# if the inner thoughts arg doesn't exist, add it
if inner_thoughts_key not in function_params:
function_params[inner_thoughts_key] = {
"type": "string",
"description": inner_thoughts_description,
}
# make sure it's tagged as required
new_function_object = copy.deepcopy(function_object) new_function_object = copy.deepcopy(function_object)
if inner_thoughts_required and inner_thoughts_key not in required_params:
# Create a new OrderedDict with inner_thoughts as the first item required_params.append(inner_thoughts_key)
new_properties = OrderedDict() new_function_object["parameters"]["required"] = required_params
new_properties[inner_thoughts_key] = {
"type": "string",
"description": inner_thoughts_description,
}
# Add the rest of the properties
new_properties.update(function_object["parameters"]["properties"])
# Cast OrderedDict back to a regular dict
new_function_object["parameters"]["properties"] = dict(new_properties)
# Update required parameters if necessary
if inner_thoughts_required:
required_params = new_function_object["parameters"].get("required", [])
if inner_thoughts_key not in required_params:
required_params.insert(0, inner_thoughts_key)
new_function_object["parameters"]["required"] = required_params
new_functions.append(new_function_object) new_functions.append(new_function_object)
# return a list of copies
return new_functions return new_functions

View File

@ -140,7 +140,6 @@ def create(
raise ValueError(f"OpenAI key is missing from letta config file") raise ValueError(f"OpenAI key is missing from letta config file")
data = build_openai_chat_completions_request(llm_config, messages, user_id, functions, function_call, use_tool_naming, max_tokens) data = build_openai_chat_completions_request(llm_config, messages, user_id, functions, function_call, use_tool_naming, max_tokens)
print(f"Data.tools: {data.tools}")
if stream: # Client requested token streaming if stream: # Client requested token streaming
data.stream = True data.stream = True

View File

@ -9,11 +9,7 @@ from httpx_sse._exceptions import SSEError
from letta.constants import OPENAI_CONTEXT_WINDOW_ERROR_SUBSTRING from letta.constants import OPENAI_CONTEXT_WINDOW_ERROR_SUBSTRING
from letta.errors import LLMError from letta.errors import LLMError
from letta.llm_api.helpers import ( from letta.llm_api.helpers import add_inner_thoughts_to_functions, make_post_request
add_inner_thoughts_to_functions,
convert_to_structured_output,
make_post_request,
)
from letta.local_llm.constants import ( from letta.local_llm.constants import (
INNER_THOUGHTS_KWARG, INNER_THOUGHTS_KWARG,
INNER_THOUGHTS_KWARG_DESCRIPTION, INNER_THOUGHTS_KWARG_DESCRIPTION,
@ -116,7 +112,7 @@ def build_openai_chat_completions_request(
use_tool_naming: bool, use_tool_naming: bool,
max_tokens: Optional[int], max_tokens: Optional[int],
) -> ChatCompletionRequest: ) -> ChatCompletionRequest:
if functions and llm_config.put_inner_thoughts_in_kwargs: if llm_config.put_inner_thoughts_in_kwargs:
functions = add_inner_thoughts_to_functions( functions = add_inner_thoughts_to_functions(
functions=functions, functions=functions,
inner_thoughts_key=INNER_THOUGHTS_KWARG, inner_thoughts_key=INNER_THOUGHTS_KWARG,
@ -158,8 +154,8 @@ def build_openai_chat_completions_request(
) )
# https://platform.openai.com/docs/guides/text-generation/json-mode # https://platform.openai.com/docs/guides/text-generation/json-mode
# only supported by gpt-4o, gpt-4-turbo, or gpt-3.5-turbo # only supported by gpt-4o, gpt-4-turbo, or gpt-3.5-turbo
# if "gpt-4o" in llm_config.model or "gpt-4-turbo" in llm_config.model or "gpt-3.5-turbo" in llm_config.model: if "gpt-4o" in llm_config.model or "gpt-4-turbo" in llm_config.model or "gpt-3.5-turbo" in llm_config.model:
# data.response_format = {"type": "json_object"} data.response_format = {"type": "json_object"}
if "inference.memgpt.ai" in llm_config.model_endpoint: if "inference.memgpt.ai" in llm_config.model_endpoint:
# override user id for inference.memgpt.ai # override user id for inference.memgpt.ai
@ -465,13 +461,6 @@ def openai_chat_completions_request_stream(
data.pop("tools") data.pop("tools")
data.pop("tool_choice", None) # extra safe, should exist always (default="auto") data.pop("tool_choice", None) # extra safe, should exist always (default="auto")
if "tools" in data:
for tool in data["tools"]:
# tool["strict"] = True
tool["function"] = convert_to_structured_output(tool["function"])
print(f"\n\n\n\nData[tools]: {json.dumps(data['tools'], indent=2)}")
printd(f"Sending request to {url}") printd(f"Sending request to {url}")
try: try:
return _sse_post(url=url, data=data, headers=headers) return _sse_post(url=url, data=data, headers=headers)