Run a complete example
Install the client and setPERCEPTRON_API_KEY to your API key:
inventory.py and run python inventory.py. The inventory is a local example dataset, so only the model request needs network access. Replace lookup_inventory with your own database or service once the loop is working.
run() returns the completed conversation, including the final assistant answer, so another request can reuse the tool results.
Preserve the conversation
Append the whole assistant message before its tool results. The SDK accepts the returnedresponse.message directly in messages; use its to_dict() method when storing history as dictionaries, as in the example. Both retain content, tool_calls, and any returned reasoning_content.
The model’s next input does not necessarily include every saved field. Historical reasoning_content is forwarded when the request includes function tools or tool-call/result history; it is ignored on ordinary requests without either. When an assistant message contains tool calls, its accompanying prose in content is omitted from the next model input. Keep the full message for the tool round trip, and put information needed for subsequent reasoning in the user messages or tool results.
Each call needs one result with the matching tool_call_id before you continue. Results may be supplied in a different order from the calls. Keep function.arguments as the returned string in assistant history, even after parsing it for execution.
Continue sending the same function declarations on subsequent requests, including when you want a final answer. For a constrained JSON or regex answer, complete the tool loop first, then omit the tool declarations and options from that separate request. Assistant text and tool arguments can be absent or empty while a response is streaming.
Stream a tool call
Replacecomplete_turn in the example with this version. The execution loop stays the same. The SDK assembles calls by index and accumulates argument fragments. The example waits for the complete stream and checks the result before returning anything that can be executed.
stream_options.include_usage: true explicitly. Usage can arrive in a separate trailing chunk with choices: []. get_final_completion() consumes any remaining chunks and returns the assembled message and usage. If you read chunks yourself, consume usage independently of choice deltas and keep reading after a finish reason.
finish_reason: "tool_calls" means a model turn is complete and ready for your application to handle. "stop" is a completed answer. Treat "length", a missing finish reason, or an API error as an incomplete turn; do not execute accumulated arguments. The SDK raises an SDKError subclass for API or transport failures, and IncompleteStreamError when the stream ends without [DONE]. Its error may carry a .partial completion for diagnostics; do not execute those partial calls. A raw SSE client must also handle {"error": ...} events, which end the stream without [DONE].
Supported controls
The API behaviors below apply when the request declares non-emptytools. The SDK validates controls before sending a request: unsupported tool_choice values and incompatible tool/output combinations raise BadRequestError locally. At the HTTP API layer, validly shaped tool_choice and parallel_tool_calls values are ignored when tools is omitted or empty.
For a machine-readable final report, finish the tool workflow and make a separate structured-output request without tool declarations. Completed tool history can remain in the conversation. For execution budgets, image results, and retrieval patterns, continue to Building a tool agent.