1) Use UserConfig to change system default LLM model name

2)  Support GPT4-Turbo JSON resp format
This commit is contained in:
Liu Zhicong
2023-11-13 16:07:33 -08:00
parent 763360b305
commit 5fe3073cb6
6 changed files with 86 additions and 27 deletions
+13 -8
View File
@@ -518,15 +518,17 @@ class AIAgent:
final_result = task_result.result_str
if final_result is not None:
if final_result[0] == "{":
llm_result = LLMResult.from_json_str(final_result)
else:
llm_result : LLMResult = LLMResult.from_str(final_result)
llm_result : LLMResult = LLMResult.from_str(final_result)
else:
llm_result = LLMResult()
llm_result.state = "ignore"
final_result = llm_result.resp
if llm_result.resp is None:
if llm_result.raw_resp:
final_result = json.dumps(llm_result.raw_resp)
else:
final_result = llm_result.resp
await workspace.exec_op_list(llm_result.op_list,self.agent_id)
@@ -866,7 +868,7 @@ class AIAgent:
prompt.append(todo.detail)
prompt.append(todo.result)
task_result:ComputeTaskResult = await self._do_llm_complection(prompt,workspace.get_inner_functions())
task_result:ComputeTaskResult = await self._do_llm_complection(prompt,workspace.get_inner_functions(),None,True)
if task_result.result_code != ComputeTaskResultCode.OK:
logger.error(f"_llm_check_todo compute error:{task_result.error_str}")
@@ -1129,10 +1131,13 @@ class AIAgent:
return known_info,result_token_len
return None,0
async def _do_llm_complection(self,prompt:AgentPrompt,inner_functions:dict=None,org_msg:AgentMsg=None) -> ComputeTaskResult:
async def _do_llm_complection(self,prompt:AgentPrompt,inner_functions:dict=None,org_msg:AgentMsg=None,is_json_resp = False) -> ComputeTaskResult:
from .compute_kernel import ComputeKernel
#logger.debug(f"Agent {self.agent_id} do llm token static system:{system_prompt_len},function:{function_token_len},history:{history_token_len},input:{input_len}, totoal prompt:{system_prompt_len + function_token_len + history_token_len} ")
task_result:ComputeTaskResult = await ComputeKernel.get_instance().do_llm_completion(prompt,self.llm_model_name,self.max_token_size,inner_functions)
if is_json_resp:
task_result:ComputeTaskResult = await ComputeKernel.get_instance().do_llm_completion(prompt,"json",self.llm_model_name,self.max_token_size,inner_functions)
else:
task_result:ComputeTaskResult = await ComputeKernel.get_instance().do_llm_completion(prompt,"text",self.llm_model_name,self.max_token_size,inner_functions)
if task_result.result_code != ComputeTaskResultCode.OK:
logger.error(f"_do_llm_complection llm compute error:{task_result.error_str}")
#error_resp = msg.create_error_resp(task_result.error_str)
+3
View File
@@ -237,8 +237,10 @@ class LLMResult:
def __init__(self) -> None:
self.state : str = "ignore"
self.resp : str = ""
self.raw_resp = None
self.paragraphs : dict[str,FunctionItem] = []
self.post_msgs : List[AgentMsg] = []
self.send_msgs : List[AgentMsg] = []
self.calls : List[FunctionItem] = []
@@ -257,6 +259,7 @@ class LLMResult:
llm_json = json.loads(llm_json_str)
r.state = llm_json.get("state")
r.resp = llm_json.get("resp")
r.raw_resp = llm_json
post_msgs = llm_json.get("post_msg")
r.post_msgs = []
+4 -4
View File
@@ -119,11 +119,11 @@ class ComputeKernel:
# friendly interface for use:
def llm_completion(self, prompt: AgentPrompt, mode_name: Optional[str] = None, max_token: int = 0,inner_functions = None):
def llm_completion(self, prompt: AgentPrompt, resp_mode:str="text",mode_name: Optional[str] = None, max_token: int = 0,inner_functions = None):
# craete a llm_work_task ,push on queue's end
# then task_schedule would run this task.(might schedule some work_task to another host)
task_req = ComputeTask()
task_req.set_llm_params(prompt, mode_name, max_token,inner_functions)
task_req.set_llm_params(prompt,resp_mode,mode_name, max_token,inner_functions)
self.run(task_req)
return task_req
@@ -155,8 +155,8 @@ class ComputeKernel:
return time_out_result
async def do_llm_completion(self, prompt: AgentPrompt, mode_name: Optional[str] = None, max_token: int = 0, inner_functions = None) -> str:
task_req = self.llm_completion(prompt, mode_name, max_token,inner_functions)
async def do_llm_completion(self, prompt: AgentPrompt,resp_mode:str="text", mode_name: Optional[str] = None, max_token: int = 0, inner_functions = None) -> str:
task_req = self.llm_completion(prompt, resp_mode,mode_name, max_token,inner_functions)
return await self._wait_task(task_req)
+7 -5
View File
@@ -4,6 +4,7 @@ import uuid
import time
from typing import Union
from knowledge import ObjectID
from .storage import AIStorage
class ComputeTaskResultCode(Enum):
OK = 0
@@ -45,16 +46,16 @@ class ComputeTask:
self.result = None
self.error_str = None
def set_llm_params(self, prompts, model_name, max_token_size, inner_functions = None, callchain_id=None):
def set_llm_params(self, prompts, resp_mode,model_name, max_token_size, inner_functions = None, callchain_id=None):
self.task_type = ComputeTaskType.LLM_COMPLETION
self.create_time = time.time()
self.task_id = uuid.uuid4().hex
self.callchain_id = callchain_id
self.params["prompts"] = prompts.to_message_list()
if model_name is not None:
self.params["model_name"] = model_name
else:
self.params["model_name"] = "gpt-4-0613"
self.params["resp_mode"] = resp_mode
if model_name is None:
model_name = AIStorage.get_instance().get_user_config().get_value("llm_model_name")
self.params["model_name"] = model_name
if max_token_size is None:
self.params["max_token_size"] = 4000
else:
@@ -116,6 +117,7 @@ class ComputeTaskResult:
self.result_refers: dict = {}
self.pading_data: bytearray = None
def set_from_task(self, task: ComputeTask):
self.task_id = task.task_id
self.callchain_id = task.callchain_id
+48 -10
View File
@@ -1,4 +1,5 @@
import openai
from openai import AsyncOpenAI
import os
import asyncio
from asyncio import Queue
@@ -59,6 +60,35 @@ class OpenAI_ComputeNode(ComputeNode):
async def remove_task(self, task_id: str):
pass
def message_to_dict(self, message)->dict:
result = message.dict()
# result_msg = {}
# #message.json()
# if message.content:
# result_msg["content"] = message.content
# result_msg["role"] = message.role
# if message.function_call:
# function_call = {}
# function_call["arguments"] = message.function_call.arguments
# function_call["name"] = message.function_call.name
# result_msg["function_call"] = function_call
# if message.tool_calls:
# tool_calls = []
# for tool_call in message.tool_calls:
# tool_call_dict = {}
# tool_call_dict["id"] = tool_call.id
# tool_call_dict["type"] = tool_call.type
# func_call_dict = {}
# func_call_dict["name"] = tool_call.function.name
# func_call_dict["arguments"] = tool_call.function.arguments
# tool_call_dict["function"] = func_call_dict
# tool_calls.append(tool_call_dict)
# result_msg["tool_calls"] = message.tool_calls
# result["message"] = result_msg
return result
async def _run_task(self, task: ComputeTask):
task.state = ComputeTaskState.RUNNING
@@ -107,27 +137,34 @@ class OpenAI_ComputeNode(ComputeNode):
case ComputeTaskType.LLM_COMPLETION:
mode_name = task.params["model_name"]
prompts = task.params["prompts"]
resp_mode = task.params["resp_mode"]
if resp_mode == "json":
response_format = { "type": "json_object" }
else:
response_format = None
max_token_size = task.params.get("max_token_size")
llm_inner_functions = task.params.get("inner_functions")
if max_token_size is None:
max_token_size = 4000
result_token = max_token_size
client = AsyncOpenAI()
try:
if llm_inner_functions is None:
logger.info(f"call openai {mode_name} prompts: {prompts}")
resp = await openai.ChatCompletion.acreate(model=mode_name,
resp = await client.chat.completions.create(model=mode_name,
messages=prompts,
response_format = response_format,
#max_tokens=result_token,
temperature=0.7)
)
else:
logger.info(f"call openai {mode_name} prompts: \n\t {prompts} \nfunctions: \n\t{json.dumps(llm_inner_functions)}")
resp = await openai.ChatCompletion.acreate(model=mode_name,
resp = await client.chat.completions.create(model=mode_name,
messages=prompts,
response_format = response_format,
functions=llm_inner_functions,
#max_tokens=result_token,
temperature=0.7) # TODO: add temperature to task params?
) # TODO: add temperature to task params?
except Exception as e:
logger.error(f"openai run LLM_COMPLETION task error: {e}")
task.state = ComputeTaskState.ERROR
@@ -135,10 +172,10 @@ class OpenAI_ComputeNode(ComputeNode):
result.error_str = str(e)
return result
logger.info(f"openai response: {json.dumps(resp, indent=4)}")
logger.info(f"openai response: {resp}")
status_code = resp.choices[0].finish_reason
token_usage = resp.usage
status_code = resp["choices"][0]["finish_reason"]
token_usage = resp.get("usage")
match status_code:
case "function_call":
task.state = ComputeTaskState.DONE
@@ -153,8 +190,9 @@ class OpenAI_ComputeNode(ComputeNode):
result.result_code = ComputeTaskResultCode.OK
result.worker_id = self.node_id
result.result_str = resp["choices"][0]["message"]["content"]
result.result["message"] = resp["choices"][0]["message"]
result.result_str = resp.choices[0].message.content
result.result["message"] = self.message_to_dict(resp.choices[0].message)
if token_usage:
result.result_refers["token_usage"] = token_usage
+11
View File
@@ -40,6 +40,17 @@ class UserConfig:
self.config_table = {}
self.user_config_path:str = None
self._init_default_value("llm_model_name","gpt-4-1106-preview")
def _init_default_value(self,key:str,value:Any) -> None:
if self.config_table.get(key) is not None:
logger.warning("user config key %s already exist, will be overrided",key)
new_config_item = UserConfigItem()
new_config_item.default_value = value
new_config_item.is_optional = True
self.config_table[key] = new_config_item
def add_user_config(self,key:str,desc:str,is_optional:bool,default_value:Any=None,item_type="str") -> None:
if self.config_table.get(key) is not None: