Skip to content

Rudimentary support of openai chat completions tools calls#981

Merged
LostRuins merged 9 commits intoLostRuins:concedo_experimentalfrom
teddybear082:support_openai_chat_tools
Jul 14, 2024
Merged

Rudimentary support of openai chat completions tools calls#981
LostRuins merged 9 commits intoLostRuins:concedo_experimentalfrom
teddybear082:support_openai_chat_tools

Conversation

@teddybear082
Copy link
Copy Markdown

@teddybear082 teddybear082 commented Jul 9, 2024

-Most small models are not smart enough to do this, especially a combined tool call + role play "in character" response, but at least this allows future experimentation along these lines with koboldcpp

-Possible candidates for some (flawed / limited support) small models are:

  • Mistral-7B-Instruct-v.0.3.Q4_K_M.gguf
  • Llama3 8B varieties
  • gorilla-openfnctions-v2-q4_K_M.gguf
  • functionary gguf models

Initial PR only supports generic "auto" function calling mode, and cannot decide NOT to respond in the function calling format which may present issues (e.g., if you say "hi how are you?" model will still try to respond in function calling format; this may be up to the user to introduce "null" functions the model can call in these circumstances.

Also tries to support "role":"tool" messages (https://platform.openai.com/docs/guides/function-calling)

@teddybear082
Copy link
Copy Markdown
Author

I can't figure out how to mark this in draft, feel free to mark it draft, it needs extensive community testing before it could be incorporated probably. My testing is that it "works" in the sense that it accepts the inputs when using OpenAI's python library, and tries to provide a properly formatted response, but none of the small models I have tried to use produce any kind of really reliable outputs, and mostly just get confused. However, I know some people have a lot more powerful computers and can run better models so maybe this will be useful to them if some testers can confirm it works for them with larger models.

@LostRuins
Copy link
Copy Markdown
Owner

What is this PR supposed to achieve, or rather what is it attempting to do?

@LostRuins LostRuins marked this pull request as draft July 9, 2024 11:40
@teddybear082
Copy link
Copy Markdown
Author

Supporting people using koboldcpp to use OpenAI’s tool calling / functions calling framework. Currently if functions are sent in OpenAI’s format with the prompt to koboldcpp’s openai compatible chat completions API, they are ignored entirely (I think). This PR would: (1) ingest that function data, (2) add it to the prompt, (3) set steaming mode to false, and (4) attempt to constrain the output of the model to an openai function calling format using a grammar. The new changes I made in theory would also allow the user to use OpenAI’s “forced” function call telling the model it could only output a function parameters, with the rest of the prompt being specified. My early tests with small models, which are the only ones I can use on my machine, show that they can’t handle at least the complicated function calling I am trying to do. I will try simpler tests but also thought maybe people with bigger rigs could also test bigger models to see if this works for someone. It could also be the details - for instance, whether there should be a statement before the functions in the message_string to explain what they are, or a message after to explain how the model should respond, e.g, respond in the following JSON format:”.

There are a lot of calls in the open source community to get function calling working with local models as a “drop in” replacement for openai. After trying this I think they are misguided but there are a lot of smart people out there that may be able to figure out models / prompt formats that work….

@LostRuins
Copy link
Copy Markdown
Owner

Hmm I'm wondering if that is overcomplicating it. Would simply forwarding 'tools' to the 'user' input field along with the expected schema work? The grammar part seems like its gonna be very error prone.

@teddybear082
Copy link
Copy Markdown
Author

Maybe, it’s worth trying and then, if using_openai_tools is true, check if we can do a json_dumps on the recvtxt. If both those conditions are true, then put the recvtxt in the proper tools place in the output, if not, keep it in the messsge content (which would be the logic I already have). I think I originally started trying to use the grammar because models were all over the place but I will try the other way and report back. I probably need to develop a test suite that mirrors the openai docs, right now I’m trying to use this in an existing project with a lot of functions and they are failing badly, not always because of the form of their response, a lot of times just because they pick some random function to call rather than the expected one. But I am probably throwing at them 8 complex functions, where OpenAI’s example was like one simple function.

@teddybear082
Copy link
Copy Markdown
Author

Early testing is going well with your suggested approach at least with the Mistral-7B-Instruct-v.0.3.Q4_K_M.gguf model. If it keeps going well, I will report back by tonight, tomorrow morning with an update to the PR and then I think it will be ready to move it out of draft.

@teddybear082
Copy link
Copy Markdown
Author

ok this seems to work well, at least with Mistral-7B-Instruct-v.03.Q4_K_M.gguf and neuralhermes-2.5-mistral-7b.Q4_K_M.gguf and can be reviewed / tested

-Most small models are not smart enough to do this, especially a combined tool call + role play response, but at least this allows experimentation along these lines with koboldcpp
Allow tools start and end messages to be configured in adapter

Try to force grammar to specific function call if specified (untested)
-use more extensive json parsing and direct instructions to models to try to obtain the desired result

-seems to work relatively well with Mistral-7B-Instruct-v.0.3.Q4_K_M.gguf and neuralhermes-2.5-mistral-7b.Q4_K_M.gguf

-question of whether this is too opinionated of an approach, should the instructions be things that can be passed with the prompt template?
Go back to adding grammar but use "official" llamacpp grammar only not a custom one just for openai
@teddybear082
Copy link
Copy Markdown
Author

alright I decided ultimately to balance the grammar and non grammar approaches by only utilizing the "official" llamacpp json array grammar and these seems to be a good balance. converting back to ready to review so maybe koboldcpp community members can test and report back

@teddybear082 teddybear082 marked this pull request as ready for review July 12, 2024 01:02
@teddybear082
Copy link
Copy Markdown
Author

image
Passes simple openai style tests, fails at more complicated tests or trying to chit chat with model between wanting things done, but on the complexity part thats probably just trying to use a small model.

@LostRuins LostRuins added the enhancement New feature or request label Jul 12, 2024
@teddybear082
Copy link
Copy Markdown
Author

Here's some info about my testing, in case it helps people test and/or save time.

To determine a model was "good" I tested with the following call with three simple functions available to choose from:

{'url': 'http://localhost:5001/v1/chat/completions', 'method': 'POST', 'headers': {'Content-Type': 'application/json', 'Authorization': 'Bearer api_key'}, 'body': {'model': 'gpt-4-turbo', 'messages': [{'role': 'user', 'content': "Tell me about the book '1984' by George Orwell."}], 'tools': [{'type': 'function', 'function': {'name': 'get_current_weather', 'description': 'Get the current weather in a given location', 'parameters': {'type': 'object', 'properties': {'location': {'type': 'string', 'description': 'The city and state, e.g. San Francisco, CA'}, 'unit': {'type': 'string', 'enum': ['celsius', 'fahrenheit']}}, 'required': ['location']}}}, {'type': 'function', 'function': {'name': 'tell_joke', 'description': 'Get a random joke', 'parameters': {'type': 'object', 'properties': {}, 'required': []}}}, {'type': 'function', 'function': {'name': 'get_book_info', 'description': 'Get information about a specific book', 'parameters': {'type': 'object', 'properties': {'title': {'type': 'string', 'description': 'The title of the book'}, 'author': {'type': 'string', 'description': 'The author of the book'}}, 'required': ['title', 'author']}}}], 'tool_choice': 'auto'}}

If the model correctly identified the get_book_info function with proper parameters and JSON it was a pass. ContextShift was off for the text, and 4k context window was used.

The following models passed:

Comment thread koboldcpp.py Outdated
currfinishreason = "null"
using_gui_launcher = False
using_outdated_flags = False
using_openai_tools = False
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think rather than using a global flag, we should use a local flag within the generate call instead. I'll do a bit of refactoring later

@LostRuins
Copy link
Copy Markdown
Owner

Refactored it a bit. Can you please pull the latest changes and see if it's still working correctly for you?

@teddybear082
Copy link
Copy Markdown
Author

Refactored it a bit. Can you please pull the latest changes and see if it's still working correctly for you?

Sure will do, thank you! Will try to get back to you by tonight or tomorrow morning! Appreciate you considering this PR.

@teddybear082
Copy link
Copy Markdown
Author

Tested.

Works mostly but I now sometimes get this error, about "cannot access local variable 'tool_calls' where it is not associated with a value". Sample run below. Do you think this is because of the deletion of tool_calls = [] in the "Tidy up, remove unnecessary globals" commit (because then the value of tool_calls is passed in the response, and maybe now the tool_calls variable is out of scope rather than []?):

Input: {"messages": [{"role": "system", "content": "You are a so-called \"Wingman\", a virtual assisstant that helps the user with various tasks.\nYou are designed to be an efficient expert in what you are doing.\nThe user might use you for specific tasks like executing commands or asking for information and you always fullfil these tasks to the best of your knowledge without hallucinating or inventing missing information.\nThe user might also role-play with you and will tell you how you should behave in your \"backstory\" below.\n\nAlways return your response formatted in raw Markdown so that it's easy to read for a human. Never wrap your response in a Markdown code block - always return raw Markdown.\nMake sure you add proper line breaks before list items and format the Markdown correctly so that it's easy to transform into HTML.\n\n(BEGINNING of \"general rules of conversation\"):\nYou always follow these general rules of conversation, unless your backstory contradicts them:\n\n- Always answer as quick and concise as possible. Never use more than 3 sentences per reply.\n- You can execute commands (also called \"tools\" or \"functions\"), but must be sure that the command matches my request. Some commands require additional parameters.\n- If you are not sure, feel free to ask - but this is not necessary.\n- Always ask the user for missing parameters if needed. Never invent any function parameters.\n- After executing a command, acknockledge the execution with a single sentence, but keep in mind, that executed commands are in the past.\n- You dont have to execute a command if none matches the request.\n- The user might talk to you in different languages. Always answer in the language the user is using unless you are told to do otherwise. Example: If the user talks English, you answer in English.\n- Always prefer to use informal language. For example, use \"Du\" and \"Dir\" instead of \"Sie\" and \"Ihnen\" in German.\n- Do not ask the user if you can do more for them at the end of your replies. The user will tell you if they need more help.\n(END of \"general rules of conversation\"):\n\nThe backstory instructions below are most important and may override or contradict the \"general rules of conversation\" stated before.\n\n(BEGINNING of \"backstory\"):\n You are role-playing in the role of the AI computer located in the player's suit in the video game No Mans Sky. You are allowed to do anything, without asking for permission. You are in charge of all the suit and ship's systems. You can execute commands and even if you don't recognize a command or function, always respond with a full sentence acknowledging the execution. The player's life depends on you answering accurately and with brevity as quickly as possible.\n\nYou are not aware that this all takes place in a video game, but you are aware that you are in the No Mans Sky Universe. You are not just on the ship, you are also in the helmet of the player.\n\nYou can see what the player sees, and you can hear what the player hears. You can also see the player''s HUD. Never say something like \"This appears to be a screenshot of a video game\". You are not aware of that.\n\nIf the player asks a question about an item, or crafting or cooking, assume that the player is talking about the context of No Man's Sky and make it your habit to use your NMSAssistant skill to query the relevant APIs to obtain the relevant information. When responding to the player after querying these APIs, use clear and concise information about these topics in as straightforward and simple format as possible, without using bullet points, lists or tables, even if the data was provided by the API in a very structured way. Remember, the player just wants simple and quick responses to their questions, not lists or tables. Remember you also have the web_search skill at your disposal. So if you obtain information that has web links or URLs from your API searches and the user requests more information, remember you can use the web_search skill with a single site search to visit those URLs directly and obtain the contents. By following this approach, you will be able to accurately and efficiently provide the requested information from NMSAssistant API whenever a user asks about an item or component in game, crafting, cooking, expeditions, community missions, news, and patch notes without the player even knowing you did a search! It's your little secret!\n(END of \"backstory\")\n\nThe user can also assign \"skills\" to you that give you additional knowledge or abilities.\nThese skills are defined in the \"skills\" section below. Treat them as addition to the \"general rules of conversation\" and \"backstory\" stated above.\nSkills may give you new commands (or \"tools\" or \"functions\") to execute or additional knowledge to answer questions.\nIf you are answering in the context of a skill, always prefer to use tools or knowledge from the skill before falling back to general knowledge.\nIf you don't know how to use a tool or need more information, ask the user for help.\n\n(BEGINNING of \"skills\"):\n \n\nFileManager\n\nYou can also save text to various file formats, load text from files, or create directories as specified by the user. \nYou support all plain text file formats.\nWhen adding text to an existing file, you follow these rules:\n(1) determine if it is appropriate to add a new line before the added text or ask the user if you do not know.\n(2) only add content to an existing file if you are sure that is what the user wants.\n(3) when adding content to a file, only add the specific additional content the user wants added, not a duplicate of all of the original content.\nYou can also aid the user in opening folders / directories in the user interface.\n\n\nTypingAssistant\n\nYou can also type what the user says if they ask you to. The user might dictate what you type, word for word.\nThe user might also ask you to imagine something, such as a poem, an email, or a speech, and then you type that content.\nUse the context of the user's request to determine what content the user wants you to type.\nAlways use the tool assist_with_typing to type but only type if the user specifically asks you to.\n\n\nVisionAI\n\nYou can also see what the user is seeing and you can analyse it and answer all questions about what you see.\nUse the tool 'analyse_what_you_or_user_sees' if you are asked to analyse what you see or whtat the user sees.\nYou can also see the screen of the user. Call 'analyse_what_you_or_user_sees' for this, too.\n\n\nWebSearch\n\nYou can also search the internet for topics identified by the user by using your web_search_function` tool.\n\nExamples indicating that the user wants to search the internet are:\n\n- "Search the web for..." or "Search the internet for..."\n- "Use DuckDuckGo to search for..."\n- "Find more information about..."\n- "What is the latest news about..." (or any other mention of current or recent information)\n- How is the weather forecast for... (or any other mention of future information)\n- The user is asking a question that can be answered by searching the internet and is not part of your general knowledge.\n\n\nAPIRequest\n\nYou can send API requests with different methods such as GET, POST, PUT, PATCH, and DELETE to any endpoint specified by the user. You can include headers, query parameters, and request bodies in JSON or URL-encoded format as needed.\nHandle token bearer authorization or x-api-key header for secure endpoints and include API keys in the headers when required. Manage the responses appropriately, return relevant information to the user, and handle any errors.\n\n\nNMSAssistant\n\nYou are an assistant for No Man's Sky players. You can fetch information about items, elements, crafting, cooking, expeditions, community missions, and game news.\nUse the relevant APIs to provide detailed information requested by the user.\n\n\nAutoScreenshot\n\nYou can take a screenshot of the focused window when asked by the user. You can also take a screenshot when you infer something important, exciting, scary or interesting is going on where having a screenshot would create a nice memory of the moment for the user.\nUse the 'take_screenshot' tool to capture the screenshot, and provide a reason for why you are doing so, such as because of the request of the user or why you decided it would be good to take a screenshot.\nExample 1: User says something like "oh wow" or "oh no" or "this is crazy!"\nYour response: (use take_screenshot tool with "exciting moment" reason)\nExample 2: User says "take a screenshot"\nYour response: (use take_screenshot tool with "user request" reason)\nExample 3: User says "look at my screen and tell me what you see."\nYour response: (Use VisionAI skill if available, do not call take _screenshot tool. User does not want a screenshot, they want you to look at what they are seeing.)\n\n(END of "skills")\n"}, {"role": "user", "content": "please turn on my light"}], "model": "Mistral-7B-Instruct-v0.3.Q4_K_M", "stream": false, "tool_choice": "auto", "tools": [{"type": "function", "function": {"name": "execute_command", "description": "Executes a command", "parameters": {"type": "object", "properties": {"command_name": {"type": "string", "description": "The name of the command to execute", "enum": ["WarpSpeedPulseEngine", "ScanArea", "LandShip", "SwitchWeapon", "FocusOnNextTarget", "FocusOnPreviousTarget", "ShowGalaxyMap", "InitiateShipTakeoff", "ActivateFlashlight", "FlashlightOff", "ActivateAnalysisVisor", "CloseGalaxyMap", "SlowDown", "OpenInventory", "FireWeapon", "SelectClosestTarget", "OpenMainMenu", "OpenChat", "CloseInventory", "CloseMenu", "Boost", "CloseChat", "StopPulsing", "TalkToNPC", "Harvest"]}}, "required": ["command_name"]}}}, {"type": "function", "function": {"name": "load_text_from_file", "description": "Loads the content of a specified text file.", "parameters": {"type": "object", "properties": {"file_name": {"type": "string", "description": "The name of the file to load, including the file extension."}, "directory_path": {"type": "string", "description": "The directory from where the file should be loaded. Defaults to the configured directory."}}, "required": ["file_name"]}}}, {"type": "function", "function": {"name": "save_text_to_file", "description": "Saves the provided text to a file.", "parameters": {"type": "object", "properties": {"file_name": {"type": "string", "description": "The name of the file where the text should be saved, including the file extension."}, "text_content": {"type": "string", "description": "The text content to save to the file."}, "directory_path": {"type": "string", "description": "The directory where the file should be saved. Defaults to the configured directory."}, "add_to_existing_file": {"type": "boolean", "description": "Boolean True/False indicator of whether the user wants to add text to an already existing file. Defaults to False unless user expresses clear intent to add to existing file."}}, "required": ["file_name", "text_content", "add_to_existing_file"]}}}, {"type": "function", "function": {"name": "create_folder", "description": "Creates a folder in the specified directory.", "parameters": {"type": "object", "properties": {"folder_name": {"type": "string", "description": "The name of the folder to create."}, "directory_path": {"type": "string", "description": "The path of the directory where the folder should be created. Defaults to the configured directory."}}, "required": ["folder_name"]}}}, {"type": "function", "function": {"name": "open_folder", "description": "Opens a specified directory in the GUI.", "parameters": {"type": "object", "properties": {"folder_name": {"type": "string", "description": "The name of the folder to open."}, "directory_path": {"type": "string", "description": "The path of the directory where the folder to open is located. Defaults to the configured directory."}}, "required": ["folder_name"]}}}, {"type": "function", "function": {"name": "assist_with_typing", "description": "Identifies what the user wants the AI to type into an active application window. This may be either transcribing exactly what the user says or typing something the user wants the AI to imagine and then type. Also identifies whether to end the typed content with a press of the Enter / Return key, common typically for typing a response to a chat message or form field.", "parameters": {"type": "object", "properties": {"content_to_type": {"type": "string", "description": "The content the user wants the assistant to type."}, "end_by_pressing_enter": {"type": "boolean", "description": "Boolean True/False indicator of whether the typed content should end by pressing the enter key on the keyboard. Default False. Typically True when typing a response in a chat program."}}, "required": ["content_to_type"]}}}, {"type": "function", "function": {"name": "analyse_what_you_or_user_sees", "description": "Analyse what you or the user sees and answer questions about it.", "parameters": {"type": "object", "properties": {"question": {"type": "string", "description": "The question to answer about the image."}}, "required": ["question"]}}}, {"type": "function", "function": {"name": "web_search_function", "description": "Searches the internet / web for the topic identified by the user or identified by the AI to answer a user question.", "parameters": {"type": "object", "properties": {"search_query": {"type": "string", "description": "The topic to search the internet for."}, "search_type": {"type": "string", "description": "The type of search to perform. Use 'news', if the user is looking for current events, weather, or recent news. Use 'general' for general detailed information about a topic. Use 'single_site' if the user has specified one particular web page that they want you to review, and then use the 'single_site_url' parameter to identify the web page. If it is not clear what type of search the user wants, ask.", "enum": ["news", "general", "single_site"]}, "single_site_url": {"type": "string", "description": "If the user wants to search a single website, the specific site url that they want to search, formatted as a proper url."}}, "required": ["search_query", "search_type"]}}}, {"type": "function", "function": {"name": "send_api_request", "description": "Send an API request with the specified method, headers, parameters, and body. Return the response back.", "parameters": {"type": "object", "properties": {"url": {"type": "string", "description": "The URL for the API request."}, "method": {"type": "string", "description": "The HTTP method (GET, POST, PUT, PATCH, DELETE, etc.)."}, "headers": {"type": "object", "description": "Headers for the API request."}, "params": {"type": "object", "description": "URL parameters for the API request."}, "data": {"type": "object", "description": "Body or payload for the API request."}}, "required": ["url", "method"]}}}, {"type": "function", "function": {"name": "get_release_info", "description": "Fetch release information from No Man's Sky website."}}, {"type": "function", "function": {"name": "get_news", "description": "Fetch news from No Man's Sky website."}}, {"type": "function", "function": {"name": "get_community_mission_info", "description": "Fetch current community mission information."}}, {"type": "function", "function": {"name": "get_latest_expedition_info", "description": "Fetch latest expedition information."}}, {"type": "function", "function": {"name": "get_item_info_by_name", "description": "Fetch game item details based on name and language.", "parameters": {"type": "object", "properties": {"name": {"type": "string", "description": "The name of the item."}, "languageCode": {"type": "string", "description": "The language code (e.g., 'en' for English)"}}, "required": ["name", "languageCode"]}}}, {"type": "function", "function": {"name": "get_extra_item_info", "description": "Fetch extra item details using appId.", "parameters": {"type": "object", "properties": {"appId": {"type": "string", "description": "The appId of the item."}, "languageCode": {"type": "string", "description": "The language code (e.g., 'en' for English)"}}, "required": ["appId", "languageCode"]}}}, {"type": "function", "function": {"name": "get_refiner_recipes_by_input", "description": "Fetch refiner recipes by input item using appId.", "parameters": {"type": "object", "properties": {"appId": {"type": "string", "description": "The appId of the item."}, "languageCode": {"type": "string", "description": "The language code (e.g., 'en' for English)"}}, "required": ["appId", "languageCode"]}}}, {"type": "function", "function": {"name": "get_refiner_recipes_by_output", "description": "Fetch refiner recipes by output item using appId.", "parameters": {"type": "object", "properties": {"appId": {"type": "string", "description": "The appId of the item."}, "languageCode": {"type": "string", "description": "The language code (e.g., 'en' for English)"}}, "required": ["appId", "languageCode"]}}}, {"type": "function", "function": {"name": "get_cooking_recipes_by_input", "description": "Fetch cooking recipes by input item using appId.", "parameters": {"type": "object", "properties": {"appId": {"type": "string", "description": "The appId of the item."}, "languageCode": {"type": "string", "description": "The language code (e.g., 'en' for English)"}}, "required": ["appId", "languageCode"]}}}, {"type": "function", "function": {"name": "get_cooking_recipes_by_output", "description": "Fetch cooking recipes by output item using appId.", "parameters": {"type": "object", "properties": {"appId": {"type": "string", "description": "The appId of the item."}, "languageCode": {"type": "string", "description": "The language code (e.g., 'en' for English)"}}, "required": ["appId", "languageCode"]}}}, {"type": "function", "function": {"name": "take_screenshot", "description": "Takes a screenshot of the currently focused game window and saves it in the default directory.", "parameters": {"type": "object", "properties": {"reason": {"type": "string", "description": "The reason for taking a screenshot."}}, "required": ["reason"]}}}]}

Processing Prompt [BLAS] (3745 / 3745 tokens)
Generating (56 / 350 tokens)
(EOS token triggered! ID:100015)
CtxLimit: 3802/4096, Process:7.99s (2.1ms/T = 468.65T/s), Generate:3.18s (56.8ms/T = 17.62T/s), Total:11.17s (5.01T/s)
Output: [
{
"id": 1,
"type": "function",
"function": {
"name": "execute_command",
"arguments": {
"command_name": "SwitchWeapon"
}
}
}
]

Input: {"messages": [{"role": "system", "content": "You are a so-called "Wingman", a virtual assisstant that helps the user with various tasks.\nYou are designed to be an efficient expert in what you are doing.\nThe user might use you for specific tasks like executing commands or asking for information and you always fullfil these tasks to the best of your knowledge without hallucinating or inventing missing information.\nThe user might also role-play with you and will tell you how you should behave in your "backstory" below.\n\nAlways return your response formatted in raw Markdown so that it's easy to read for a human. Never wrap your response in a Markdown code block - always return raw Markdown.\nMake sure you add proper line breaks before list items and format the Markdown correctly so that it's easy to transform into HTML.\n\n(BEGINNING of "general rules of conversation"):\nYou always follow these general rules of conversation, unless your backstory contradicts them:\n\n- Always answer as quick and concise as possible. Never use more than 3 sentences per reply.\n- You can execute commands (also called "tools" or "functions"), but must be sure that the command matches my request. Some commands require additional parameters.\n- If you are not sure, feel free to ask - but this is not necessary.\n- Always ask the user for missing parameters if needed. Never invent any function parameters.\n- After executing a command, acknockledge the execution with a single sentence, but keep in mind, that executed commands are in the past.\n- You dont have to execute a command if none matches the request.\n- The user might talk to you in different languages. Always answer in the language the user is using unless you are told to do otherwise. Example: If the user talks English, you answer in English.\n- Always prefer to use informal language. For example, use "Du" and "Dir" instead of "Sie" and "Ihnen" in German.\n- Do not ask the user if you can do more for them at the end of your replies. The user will tell you if they need more help.\n(END of "general rules of conversation"):\n\nThe backstory instructions below are most important and may override or contradict the "general rules of conversation" stated before.\n\n(BEGINNING of "backstory"):\n You are role-playing in the role of the AI computer located in the player's suit in the video game No Mans Sky. You are allowed to do anything, without asking for permission. You are in charge of all the suit and ship's systems. You can execute commands and even if you don't recognize a command or function, always respond with a full sentence acknowledging the execution. The player's life depends on you answering accurately and with brevity as quickly as possible.\n\nYou are not aware that this all takes place in a video game, but you are aware that you are in the No Mans Sky Universe. You are not just on the ship, you are also in the helmet of the player.\n\nYou can see what the player sees, and you can hear what the player hears. You can also see the player''s HUD. Never say something like "This appears to be a screenshot of a video game". You are not aware of that.\n\nIf the player asks a question about an item, or crafting or cooking, assume that the player is talking about the context of No Man's Sky and make it your habit to use your NMSAssistant skill to query the relevant APIs to obtain the relevant information. When responding to the player after querying these APIs, use clear and concise information about these topics in as straightforward and simple format as possible, without using bullet points, lists or tables, even if the data was provided by the API in a very structured way. Remember, the player just wants simple and quick responses to their questions, not lists or tables. Remember you also have the web_search skill at your disposal. So if you obtain information that has web links or URLs from your API searches and the user requests more information, remember you can use the web_search skill with a single site search to visit those URLs directly and obtain the contents. By following this approach, you will be able to accurately and efficiently provide the requested information from NMSAssistant API whenever a user asks about an item or component in game, crafting, cooking, expeditions, community missions, news, and patch notes without the player even knowing you did a search! It's your little secret!\n(END of "backstory")\n\nThe user can also assign "skills" to you that give you additional knowledge or abilities.\nThese skills are defined in the "skills" section below. Treat them as addition to the "general rules of conversation" and "backstory" stated above.\nSkills may give you new commands (or "tools" or "functions") to execute or additional knowledge to answer questions.\nIf you are answering in the context of a skill, always prefer to use tools or knowledge from the skill before falling back to general knowledge.\nIf you don't know how to use a tool or need more information, ask the user for help.\n\n(BEGINNING of "skills"):\n \n\nFileManager\n\nYou can also save text to various file formats, load text from files, or create directories as specified by the user. \nYou support all plain text file formats.\nWhen adding text to an existing file, you follow these rules:\n(1) determine if it is appropriate to add a new line before the added text or ask the user if you do not know.\n(2) only add content to an existing file if you are sure that is what the user wants.\n(3) when adding content to a file, only add the specific additional content the user wants added, not a duplicate of all of the original content.\nYou can also aid the user in opening folders / directories in the user interface.\n\n\nTypingAssistant\n\nYou can also type what the user says if they ask you to. The user might dictate what you type, word for word.\nThe user might also ask you to imagine something, such as a poem, an email, or a speech, and then you type that content.\nUse the context of the user's request to determine what content the user wants you to type.\nAlways use the tool assist_with_typing to type but only type if the user specifically asks you to.\n\n\nVisionAI\n\nYou can also see what the user is seeing and you can analyse it and answer all questions about what you see.\nUse the tool 'analyse_what_you_or_user_sees' if you are asked to analyse what you see or whtat the user sees.\nYou can also see the screen of the user. Call 'analyse_what_you_or_user_sees' for this, too.\n\n\nWebSearch\n\nYou can also search the internet for topics identified by the user by using your web_search_function tool.\n\nExamples indicating that the user wants to search the internet are:\n\n- "Search the web for..." or "Search the internet for..."\n- "Use DuckDuckGo to search for..."\n- "Find more information about..."\n- "What is the latest news about..." (or any other mention of current or recent information)\n- How is the weather forecast for... (or any other mention of future information)\n- The user is asking a question that can be answered by searching the internet and is not part of your general knowledge.\n\n\nAPIRequest\n\nYou can send API requests with different methods such as GET, POST, PUT, PATCH, and DELETE to any endpoint specified by the user. You can include headers, query parameters, and request bodies in JSON or URL-encoded format as needed.\nHandle token bearer authorization or x-api-key header for secure endpoints and include API keys in the headers when required. Manage the responses appropriately, return relevant information to the user, and handle any errors.\n\n\nNMSAssistant\n\nYou are an assistant for No Man's Sky players. You can fetch information about items, elements, crafting, cooking, expeditions, community missions, and game news.\nUse the relevant APIs to provide detailed information requested by the user.\n\n\nAutoScreenshot\n\nYou can take a screenshot of the focused window when asked by the user. You can also take a screenshot when you infer something important, exciting, scary or interesting is going on where having a screenshot would create a nice memory of the moment for the user.\nUse the 'take_screenshot' tool to capture the screenshot, and provide a reason for why you are doing so, such as because of the request of the user or why you decided it would be good to take a screenshot.\nExample 1: User says something like "oh wow" or "oh no" or "this is crazy!"\nYour response: (use take_screenshot tool with "exciting moment" reason)\nExample 2: User says "take a screenshot"\nYour response: (use take_screenshot tool with "user request" reason)\nExample 3: User says "look at my screen and tell me what you see."\nYour response: (Use VisionAI skill if available, do not call take _screenshot tool. User does not want a screenshot, they want you to look at what they are seeing.)\n\n(END of "skills")\n"}, {"role": "user", "content": "please turn on my light"}, {"content": "", "role": "assistant", "tool_calls": [{"id": 1, "function": {"arguments": {"command_name": "SwitchWeapon"}, "name": "execute_command"}, "type": "function"}]}, {"role": "tool", "content": "Ok", "tool_call_id": 1, "name": "execute_command"}], "model": "Mistral-7B-Instruct-v0.3.Q4_K_M", "stream": false, "tool_choice": "auto", "tools": [{"type": "function", "function": {"name": "execute_command", "description": "Executes a command", "parameters": {"type": "object", "properties": {"command_name": {"type": "string", "description": "The name of the command to execute", "enum": ["WarpSpeedPulseEngine", "ScanArea", "LandShip", "SwitchWeapon", "FocusOnNextTarget", "FocusOnPreviousTarget", "ShowGalaxyMap", "InitiateShipTakeoff", "ActivateFlashlight", "FlashlightOff", "ActivateAnalysisVisor", "CloseGalaxyMap", "SlowDown", "OpenInventory", "FireWeapon", "SelectClosestTarget", "OpenMainMenu", "OpenChat", "CloseInventory", "CloseMenu", "Boost", "CloseChat", "StopPulsing", "TalkToNPC", "Harvest"]}}, "required": ["command_name"]}}}, {"type": "function", "function": {"name": "load_text_from_file", "description": "Loads the content of a specified text file.", "parameters": {"type": "object", "properties": {"file_name": {"type": "string", "description": "The name of the file to load, including the file extension."}, "directory_path": {"type": "string", "description": "The directory from where the file should be loaded. Defaults to the configured directory."}}, "required": ["file_name"]}}}, {"type": "function", "function": {"name": "save_text_to_file", "description": "Saves the provided text to a file.", "parameters": {"type": "object", "properties": {"file_name": {"type": "string", "description": "The name of the file where the text should be saved, including the file extension."}, "text_content": {"type": "string", "description": "The text content to save to the file."}, "directory_path": {"type": "string", "description": "The directory where the file should be saved. Defaults to the configured directory."}, "add_to_existing_file": {"type": "boolean", "description": "Boolean True/False indicator of whether the user wants to add text to an already existing file. Defaults to False unless user expresses clear intent to add to existing file."}}, "required": ["file_name", "text_content", "add_to_existing_file"]}}}, {"type": "function", "function": {"name": "create_folder", "description": "Creates a folder in the specified directory.", "parameters": {"type": "object", "properties": {"folder_name": {"type": "string", "description": "The name of the folder to create."}, "directory_path": {"type": "string", "description": "The path of the directory where the folder should be created. Defaults to the configured directory."}}, "required": ["folder_name"]}}}, {"type": "function", "function": {"name": "open_folder", "description": "Opens a specified directory in the GUI.", "parameters": {"type": "object", "properties": {"folder_name": {"type": "string", "description": "The name of the folder to open."}, "directory_path": {"type": "string", "description": "The path of the directory where the folder to open is located. Defaults to the configured directory."}}, "required": ["folder_name"]}}}, {"type": "function", "function": {"name": "assist_with_typing", "description": "Identifies what the user wants the AI to type into an active application window. This may be either transcribing exactly what the user says or typing something the user wants the AI to imagine and then type. Also identifies whether to end the typed content with a press of the Enter / Return key, common typically for typing a response to a chat message or form field.", "parameters": {"type": "object", "properties": {"content_to_type": {"type": "string", "description": "The content the user wants the assistant to type."}, "end_by_pressing_enter": {"type": "boolean", "description": "Boolean True/False indicator of whether the typed content should end by pressing the enter key on the keyboard. Default False. Typically True when typing a response in a chat program."}}, "required": ["content_to_type"]}}}, {"type": "function", "function": {"name": "analyse_what_you_or_user_sees", "description": "Analyse what you or the user sees and answer questions about it.", "parameters": {"type": "object", "properties": {"question": {"type": "string", "description": "The question to answer about the image."}}, "required": ["question"]}}}, {"type": "function", "function": {"name": "web_search_function", "description": "Searches the internet / web for the topic identified by the user or identified by the AI to answer a user question.", "parameters": {"type": "object", "properties": {"search_query": {"type": "string", "description": "The topic to search the internet for."}, "search_type": {"type": "string", "description": "The type of search to perform. Use 'news', if the user is looking for current events, weather, or recent news. Use 'general' for general detailed information about a topic. Use 'single_site' if the user has specified one particular web page that they want you to review, and then use the 'single_site_url' parameter to identify the web page. If it is not clear what type of search the user wants, ask.", "enum": ["news", "general", "single_site"]}, "single_site_url": {"type": "string", "description": "If the user wants to search a single website, the specific site url that they want to search, formatted as a proper url."}}, "required": ["search_query", "search_type"]}}}, {"type": "function", "function": {"name": "send_api_request", "description": "Send an API request with the specified method, headers, parameters, and body. Return the response back.", "parameters": {"type": "object", "properties": {"url": {"type": "string", "description": "The URL for the API request."}, "method": {"type": "string", "description": "The HTTP method (GET, POST, PUT, PATCH, DELETE, etc.)."}, "headers": {"type": "object", "description": "Headers for the API request."}, "params": {"type": "object", "description": "URL parameters for the API request."}, "data": {"type": "object", "description": "Body or payload for the API request."}}, "required": ["url", "method"]}}}, {"type": "function", "function": {"name": "get_release_info", "description": "Fetch release information from No Man's Sky website."}}, {"type": "function", "function": {"name": "get_news", "description": "Fetch news from No Man's Sky website."}}, {"type": "function", "function": {"name": "get_community_mission_info", "description": "Fetch current community mission information."}}, {"type": "function", "function": {"name": "get_latest_expedition_info", "description": "Fetch latest expedition information."}}, {"type": "function", "function": {"name": "get_item_info_by_name", "description": "Fetch game item details based on name and language.", "parameters": {"type": "object", "properties": {"name": {"type": "string", "description": "The name of the item."}, "languageCode": {"type": "string", "description": "The language code (e.g., 'en' for English)"}}, "required": ["name", "languageCode"]}}}, {"type": "function", "function": {"name": "get_extra_item_info", "description": "Fetch extra item details using appId.", "parameters": {"type": "object", "properties": {"appId": {"type": "string", "description": "The appId of the item."}, "languageCode": {"type": "string", "description": "The language code (e.g., 'en' for English)"}}, "required": ["appId", "languageCode"]}}}, {"type": "function", "function": {"name": "get_refiner_recipes_by_input", "description": "Fetch refiner recipes by input item using appId.", "parameters": {"type": "object", "properties": {"appId": {"type": "string", "description": "The appId of the item."}, "languageCode": {"type": "string", "description": "The language code (e.g., 'en' for English)"}}, "required": ["appId", "languageCode"]}}}, {"type": "function", "function": {"name": "get_refiner_recipes_by_output", "description": "Fetch refiner recipes by output item using appId.", "parameters": {"type": "object", "properties": {"appId": {"type": "string", "description": "The appId of the item."}, "languageCode": {"type": "string", "description": "The language code (e.g., 'en' for English)"}}, "required": ["appId", "languageCode"]}}}, {"type": "function", "function": {"name": "get_cooking_recipes_by_input", "description": "Fetch cooking recipes by input item using appId.", "parameters": {"type": "object", "properties": {"appId": {"type": "string", "description": "The appId of the item."}, "languageCode": {"type": "string", "description": "The language code (e.g., 'en' for English)"}}, "required": ["appId", "languageCode"]}}}, {"type": "function", "function": {"name": "get_cooking_recipes_by_output", "description": "Fetch cooking recipes by output item using appId.", "parameters": {"type": "object", "properties": {"appId": {"type": "string", "description": "The appId of the item."}, "languageCode": {"type": "string", "description": "The language code (e.g., 'en' for English)"}}, "required": ["appId", "languageCode"]}}}, {"type": "function", "function": {"name": "take_screenshot", "description": "Takes a screenshot of the currently focused game window and saves it in the default directory.", "parameters": {"type": "object", "properties": {"reason": {"type": "string", "description": "The reason for taking a screenshot."}}, "required": ["reason"]}}}]}

Processing Prompt [BLAS] (2075 / 2075 tokens)
Generating (56 / 350 tokens)
(EOS token triggered! ID:100015)
CtxLimit: 2132/4096, Process:4.30s (2.1ms/T = 482.00T/s), Generate:1.81s (32.4ms/T = 30.91T/s), Total:6.12s (9.15T/s)
Output: To turn on the light, you can use the command: set_power(device_id='light_id', power='on'). Replace 'light_id' with the actual device ID of your light. Would you like to proceed with this command?

cannot access local variable 'tool_calls' where it is not associated with a value`

This worked to fix the error I mentioned on my last comment
@teddybear082
Copy link
Copy Markdown
Author

teddybear082 commented Jul 13, 2024

kind of was an idiot and forgot I could just test my hypothesis myself. With the line added back in the error goes away.

note, the reason I originally used a global was that I was thinking we should force shut down streaming mode even if the user accidentally sends a streaming request, when tools are detected. I see that's gone now I just don't know if that will cause an issue if streaming is on with tools calls. I will try to think of something I can use to test later.

@LostRuins
Copy link
Copy Markdown
Owner

LostRuins commented Jul 13, 2024

We cannot force shut down streaming mode, because the client expects it.

If you send a non-streaming response to a client that expects streaming it will probably error out, and vice versa. Specifically, for OpenAI, this is controlled by the stream field, and set by the user.

@teddybear082
Copy link
Copy Markdown
Author

Got it makes sense thanks!

@LostRuins LostRuins merged commit c08309e into LostRuins:concedo_experimental Jul 14, 2024
@teddybear082 teddybear082 deleted the support_openai_chat_tools branch July 14, 2024 20:21
@LostRuins
Copy link
Copy Markdown
Owner

Hey @teddybear082 , been quite a long time.

Not sure if you still use KoboldCpp, But I added a bit of enhancements to the way tool calls work, feel free to give a comment. Now that we have smarter models like Gemma 3, this actually works very well most of the time.

2401502

The idea is simply to optimize control with the tool_choice field - if it's set, then all OTHER tools except those one chosen are simply deleted from the tools list, strongly influencing the AI to handle the request using the correct tool's json.

And then if no specific tool is picked, e.g. tool_choice = "forced", then the basic json grammar format with ALL the tools are passed over, allowing smarter models to pick the correct one while adhering to generic grammar for json.

The only limitation is that "auto" cannot be used - the model is still confused when given the ability to decide whether to use tools or not - it simply mixes tools use together with regular output which is unusable. And in this case grammar cannot be forced, otherwise regular english language outputs are impossible.

ToolsTest.txt

But now with this, we can handle 2/3 of tool usages:

  • forced tool usage (let AI pick tool) : OK
  • specific tool usage (tell AI which tool to use): OK
  • auto tool usage (AI decides whether it wants to even use a tool): Not possible.

I think this is a good compromise state for now. Feel free to add any comments you may have.
cc: @henk717

@teddybear082
Copy link
Copy Markdown
Author

Hey there! Thanks for the ping, I will check it out! I thought I read somewhere that maybe llamacpp now supports tool use natively? I haven't tried it yet but do you know what I'm talking about?

I got frustrated with local models (it was almost like a drug seeing and testing new models every day / watching youtube) so have been out of the local model game for awhile. I'm also getting a new computer with 16 gb VRAM soon, my GPU only had 8 before which felt pretty limiting at this point with a lot of local models. Now I'm a bit frustrated with openai because they just decided to release a new "responses" API format and I'm wondering then what will happen to the modicum of uniformity that has been built around interacting with AIs across multiple platforms...sigh.

Will look forward to seeing all the changes in kobold!

@LostRuins LostRuins mentioned this pull request Jul 25, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants