-
-
Notifications
You must be signed in to change notification settings - Fork 15.3k
[Feature]: Support structured output and tool call together #16313
Description
🚀 The feature, motivation and pitch
Checklist
- 1. If the issue you raised is not a feature but a question, please raise a discussion at https://github.com/sgl-project/sglang/discussions/new/choose Otherwise, it will be closed.
- 2. Please use English, otherwise it will be closed.
Motivation
as discussed in: https://www.reddit.com/r/LocalLLaMA/comments/1h2y7ys/why_can_you_not_use_structured_output_and_tool/
request support for tool execution followed by structured response output, similar to how OpenAI handles function calls (Tool Calls) and Schema outputs in the gpt-4o-mini API.
OpenAI supports this capability through a workflow where:
The model can first execute one or more tool calls to perform calculations or retrieve information
After receiving the results of these tool calls, the model can then produce a final structured response conforming to a predefined schema
Example Implementation from OpenAI
Input to OpenAI
User query: "current age of University of Oxford is 928"
Available tools: add, subtract, multiply, divide, round
Response format: Schema with current_age and calculated_age fields
Execution flow:
- Model analyzes query and determines it needs to call the add function
- Model executes: add(928, 1) to calculate the next year's age
- Function returns: 929
- Model produces final structured response:
{"current_age":"928","calculated_age":"929"}
=================Input messages to OpenAI=================
[{'role': 'system', 'content': 'You are a helpful assistant. You must call tools'}, {'role': 'user', 'content': 'current age of University of Oxford is 928, '}]
=================Input config to OpenAI=================
{'temperature': 0.0, 'response_format': <class '__main__.Schema'>, 'tools': [{'type': 'function', 'function': {'name': 'add', 'description': 'Adds two numbers.', 'strict': True, 'parameters': {'properties': {'a': {'type': 'number', 'description': 'The first number to be added.'}, 'b': {'type': 'number', 'description': 'The second number to be added.'}}, 'required': ['a', 'b'], 'type': 'object', 'additionalProperties': False}}}, {'type': 'function', 'function': {'name': 'sub', 'description': 'Do subtraction between two numbers.', 'strict': True, 'parameters': {'properties': {'a': {'type': 'number', 'description': 'The minuend in subtraction.'}, 'b': {'type': 'number', 'description': 'The subtrahend in subtraction.'}}, 'required': ['a', 'b'], 'type': 'object', 'additionalProperties': False}}}, {'type': 'function', 'function': {'name': 'multiply', 'description': 'Multiplies two numbers.', 'strict': True, 'parameters': {'properties': {'a': {'type': 'number', 'description': 'The multiplier in the multiplication.'}, 'b': {'type': 'number', 'description': 'The multiplicand in the multiplication.'}, 'decimal_places': {'type': 'integer', 'description': 'The number of decimal\nplaces to round to. Defaults to 2.'}}, 'required': ['a', 'b', 'decimal_places'], 'type': 'object', 'additionalProperties': False}}}, {'type': 'function', 'function': {'name': 'divide', 'description': 'Divides two numbers.', 'strict': True, 'parameters': {'properties': {'a': {'type': 'number', 'description': 'The dividend in the division.'}, 'b': {'type': 'number', 'description': 'The divisor in the division.'}, 'decimal_places': {'type': 'integer', 'description': 'The number of\ndecimal places to round to. Defaults to 2.'}}, 'required': ['a', 'b', 'decimal_places'], 'type': 'object', 'additionalProperties': False}}}, {'type': 'function', 'function': {'name': 'round', 'description': 'Rounds a number to a specified number of decimal places.', 'strict': True, 'parameters': {'properties': {'a': {'type': 'number', 'description': 'The number to be rounded.'}, 'decimal_places': {'type': 'integer', 'description': 'The number of decimal places\nto round to. Defaults to 0.'}}, 'required': ['a', 'decimal_places'], 'type': 'object', 'additionalProperties': False}}}]}
=================Output from OpenAI=================
ParsedChatCompletion[Schema](id='chatcmpl-BJpADooa7xu5xJHJNjCJ1Je4R6Oa6', choices=[ParsedChoice[Schema](finish_reason='tool_calls', index=0, logprobs=None, message=ParsedChatCompletionMessage[Schema](content=None, refusal=None, role='assistant', annotations=[], audio=None, function_call=None, tool_calls=[ParsedFunctionToolCall(id='call_WmGiI0q8YRNojvxAQIbCg1EH', function=ParsedFunction(arguments='{"a":928,"b":1}', name='add', parsed_arguments={'a': 928, 'b': 1}), type='function')], parsed=None))], created=1744064605, model='gpt-4o-mini-2024-07-18', object='chat.completion', service_tier='default', system_fingerprint='fp_b376dfbbd5', usage=CompletionUsage(completion_tokens=18, prompt_tokens=351, total_tokens=369, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0), prompt_tokens_details=PromptTokensDetails(audio_tokens=0, cached_tokens=0)))
***************add function called*********************
=================Input messages to OpenAI=================
[{'role': 'system', 'content': 'You are a helpful assistant. You must call tools'}, {'role': 'user', 'content': 'current age of University of Oxford is 928, '}, {'role': 'assistant', 'content': '', 'tool_calls': [{'id': 'call_PaitDpihu1REKPNJnx1qtSSe', 'type': 'function', 'function': {'name': 'add', 'arguments': '{"a": 928, "b": 1}'}}]}, {'role': 'tool', 'content': '929', 'tool_call_id': 'call_PaitDpihu1REKPNJnx1qtSSe'}]
=================Input config to OpenAI=================
{'temperature': 0.0, 'response_format': <class '__main__.Schema'>, 'tools': [{'type': 'function', 'function': {'name': 'add', 'description': 'Adds two numbers.', 'strict': True, 'parameters': {'properties': {'a': {'type': 'number', 'description': 'The first number to be added.'}, 'b': {'type': 'number', 'description': 'The second number to be added.'}}, 'required': ['a', 'b'], 'type': 'object', 'additionalProperties': False}}}, {'type': 'function', 'function': {'name': 'sub', 'description': 'Do subtraction between two numbers.', 'strict': True, 'parameters': {'properties': {'a': {'type': 'number', 'description': 'The minuend in subtraction.'}, 'b': {'type': 'number', 'description': 'The subtrahend in subtraction.'}}, 'required': ['a', 'b'], 'type': 'object', 'additionalProperties': False}}}, {'type': 'function', 'function': {'name': 'multiply', 'description': 'Multiplies two numbers.', 'strict': True, 'parameters': {'properties': {'a': {'type': 'number', 'description': 'The multiplier in the multiplication.'}, 'b': {'type': 'number', 'description': 'The multiplicand in the multiplication.'}, 'decimal_places': {'type': 'integer', 'description': 'The number of decimal\nplaces to round to. Defaults to 2.'}}, 'required': ['a', 'b', 'decimal_places'], 'type': 'object', 'additionalProperties': False}}}, {'type': 'function', 'function': {'name': 'divide', 'description': 'Divides two numbers.', 'strict': True, 'parameters': {'properties': {'a': {'type': 'number', 'description': 'The dividend in the division.'}, 'b': {'type': 'number', 'description': 'The divisor in the division.'}, 'decimal_places': {'type': 'integer', 'description': 'The number of\ndecimal places to round to. Defaults to 2.'}}, 'required': ['a', 'b', 'decimal_places'], 'type': 'object', 'additionalProperties': False}}}, {'type': 'function', 'function': {'name': 'round', 'description': 'Rounds a number to a specified number of decimal places.', 'strict': True, 'parameters': {'properties': {'a': {'type': 'number', 'description': 'The number to be rounded.'}, 'decimal_places': {'type': 'integer', 'description': 'The number of decimal places\nto round to. Defaults to 0.'}}, 'required': ['a', 'decimal_places'], 'type': 'object', 'additionalProperties': False}}}]}
=================Output from OpenAI=================
ParsedChatCompletion[Schema](id='chatcmpl-BJpAEN4GchSUheJzl3bKeVKXTuD11', choices=[ParsedChoice[Schema](finish_reason='stop', index=0, logprobs=None, message=ParsedChatCompletionMessage[Schema](content='{"current_age":"928","calculated_age":"929"}', refusal=None, role='assistant', annotations=[], audio=None, function_call=None, tool_calls=None, parsed=Schema(current_age='928', calculated_age='929')))], created=1744064606, model='gpt-4o-mini-2024-07-18', object='chat.completion', service_tier='default', system_fingerprint='fp_b376dfbbd5', usage=CompletionUsage(completion_tokens=17, prompt_tokens=376, total_tokens=393, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0), prompt_tokens_details=PromptTokensDetails(audio_tokens=0, cached_tokens=0)))
{"current_age":"928","calculated_age":"929"}
Related resources
No response
Alternatives
No response
Additional context
No response
Before submitting a new issue...
- Make sure you already searched for relevant issues, and asked the chatbot living at the bottom right corner of the documentation page, which can answer lots of frequently asked questions.