This release is a pre-release and may not be stable for production use.
Chat Completions Conversation With Tools
LLM tool calling for environments that modern SDKs left behind.
Python 2. Python 3. Windows XP. Embedded CPython. Air-gapped networks. If you can run import json, you can run this package.
Why This Exists
LLM tool calling has become essential — but the ecosystem has raced toward modern stacks. The official OpenAI Python SDK requires Python 3.8+. LangChain wants 3.9+. They pull in httpx, pydantic, anyio, and a tree of native extensions. None of that runs on a Windows XP box or an old Debian embedded controller.
Meanwhile, the API itself hasn't changed: it's still JSON over HTTP, with the same OpenAI Chat Completions function-calling schema that every major provider speaks. The barrier is purely in the client libraries.
This package removes that barrier. It's a single-file, zero-dependency stateful wrapper around any OpenAI Chat Completions-compatible HTTP API. No requests. No httpx. No native code. Just urllib/urllib2, json, and the typing module — all of which ship with Python 2.7 and later.
What It Gives You
| Capability | How |
|---|---|
| Tool definitions | Python TypedDict → JSON Schema (automatic, recursive, cycle-safe) |
| Non-streaming responses | Parse tool_calls[] from the response message |
| Streaming responses | SSE line-by-line parser with tool call delta accumulation |
| Conversation history | Automatic message bookkeeping (user, assistant, tool roles) |
| Multimodal | Image URLs via OpenAI content-parts format |
| Providers | OpenAI, DeepSeek, Groq, Together, xAI, OpenRouter — anything that speaks Chat Completions |
Function Calling Convention
This package implements the OpenAI native function calling convention — an API-level protocol where tools and tool calls are first-class fields in the request/response schema. This is the industry standard, supported by OpenAI and every major compatible provider.
Request format
{
"model": "...",
"messages": [...],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Look up the weather for a city.",
"parameters": {
"type": "object",
"properties": {
"city": { "type": "string" }
},
"required": ["city"],
"additionalProperties": false
}
}
}
],
"tool_choice": "auto"
}
Response format
{
"choices": [{
"message": {
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\":\"Tokyo\"}"
}
}
]
}
}]
}
Tool results
{
"role": "tool",
"tool_call_id": "call_abc123",
"content": "Sunny, 22°C"
}
Streaming deltas
When stream: true, tool calls arrive incrementally via SSE chunks keyed by index. The package accumulates fragments and finalizes them when the stream completes.
This is an API-native convention — tools and tool calls are structural fields in the HTTP bodies, not prompt-engineered text that requires client-side regex or XML parsing.
Installation
pip install chat-completions-conversation-with-tools
No native extensions. No system dependencies. Works with pip on Python 2.7 and Python 3.x.
Usage
Actual tool execution is application-defined. Provide a callable per tool, or bridge to an external process — the package only handles the API conversation.
Basic example (OpenAI)
from typing import TypedDict
from chat_completions_conversation_with_tools import (
ChatCompletionsConversationWithTools,
Tool,
)
class WeatherArgs(TypedDict):
city: str
conversation = ChatCompletionsConversationWithTools(
api_key="sk-...",
base_url="https://api.openai.com/v1",
model="gpt-4o",
system_prompt="Call the get_weather tool when asked about weather.",
tools_by_name={
"get_weather": Tool("Look up the current weather for a city.", WeatherArgs),
},
)
# Non-streaming
response = conversation.send_and_receive_response("What's the weather in Tokyo?")
print(response.tool_calls)
# [ToolCall(id='...', name='get_weather', arguments={'city': 'Tokyo'})]
conversation.append_tool_message(response.tool_calls[0].id, "Sunny, 22°C")
final = conversation.send_and_receive_response()
print(final.content)
# "The weather in Tokyo is sunny with a temperature of 22°C."
Streaming (DeepSeek)
conversation = ChatCompletionsConversationWithTools(
api_key="sk-...",
base_url="https://api.deepseek.com",
model="deepseek-chat",
system_prompt="Call the get_weather tool when asked about weather.",
tools_by_name={
"get_weather": Tool("Look up the current weather for a city.", WeatherArgs),
},
)
response = conversation.send_and_stream_response(
text="What's the weather in Tokyo?",
on_content_delta=lambda text: print(text, end="", flush=True),
)
# Let me check the weather in Tokyo for you!
print(response.tool_calls)
# [ToolCall(id='call_00_...', name='get_weather', arguments={'city': 'Tokyo'})]
conversation.append_tool_message(response.tool_calls[0].id, "Sunny, 22°C")
final = conversation.send_and_stream_response(
on_content_delta=lambda text: print(text, end="", flush=True),
)
# The weather in Tokyo is sunny with a temperature of 22°C.
Image input
conversation.append_user_message(
"What's in this image?",
image_url="https://example.com/photo.jpg",
)
response = conversation.send_and_receive_response()
Public API
| Class | Purpose |
|---|---|
Tool(description, typeddict_class) |
Wraps a tool description and a TypedDict parameter schema. Automatically converts the TypedDict to JSON Schema. |
ToolCall |
Stores a parsed tool call: .id, .name, .arguments. |
AssistantResponse |
Stores the assistant turn: .content (text) and .tool_calls (list of ToolCall). |
ChatCompletionsConversationWithTools |
Manages message history and API communication. |
ChatCompletionsConversationWithTools
__init__(api_key, base_url, model, system_prompt, tools_by_name)
| Method | Description |
|---|---|
send_and_receive_response(text=None, image_url=None) |
Send a user message (or continue after tool results) and return the assistant response. |
send_and_stream_response(text=None, image_url=None, on_content_delta=None, on_tool_call_delta=None) |
Same, but streams the response via callbacks. |
append_user_message(text, image_url=None) |
Add a user message to history without sending. |
append_assistant_message(content, tool_calls=None) |
Add an assistant message to history. |
append_tool_message(tool_call_id, content) |
Add a tool result to history. |
reset() |
Clear conversation history and re-insert the system prompt. |
set_system_prompt(prompt, update_messages=True) |
Change the system prompt. |
build_payload(messages, stream=False) |
Build the raw API request payload (for debugging or custom transport). |
Constraints (By Design)
No requests or httpx. This package uses only urllib/urllib2 from the standard library. It installs and runs anywhere Python does — no native compilation, no system libraries.
Python 2.7 compatible. Every string, every import, every type annotation is written to work on Python 2.7 and Python 3.x from the same source file.
Single file. The entire library is one module. Drop chat_completions_conversation_with_tools.py into any project. No package structure to navigate, no implicit namespace dependencies.
No framework dependencies. No pydantic, no attrs, no dataclasses. Tool parameter schemas use standard TypedDict, automatically converted to JSON Schema at call time.
Provider Compatibility
Tested and working with:
| Provider | Model | Status |
|---|---|---|
| DeepSeek | deepseek-chat |
✅ Non-streaming + streaming |
| OpenAI | gpt-4o, gpt-4.1 |
✅ Compatible API |
| Groq, Together, xAI, OpenRouter | Any Chat Completions model | ✅ Compatible API |
| Local (vLLM, Ollama, LM Studio) | Any tool-capable model | ✅ Compatible API |
License
MIT — see LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file chat_completions_conversation_with_tools-0.1.0a1.tar.gz.
File metadata
- Download URL: chat_completions_conversation_with_tools-0.1.0a1.tar.gz
- Upload date:
- Size: 8.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
45aab943751eeaf0cc044e31a327651a4d6a6bb369731829aa4ce9f627a87407
|
|
| MD5 |
1fa34d7e2f2e4fe2c92c6362464c3dd7
|
|
| BLAKE2b-256 |
eaff1baa9b7048e0230aefc883ce3e65bffb3657779c0187596cf86cc2a2da30
|
File details
Details for the file chat_completions_conversation_with_tools-0.1.0a1-py2.py3-none-any.whl.
File metadata
- Download URL: chat_completions_conversation_with_tools-0.1.0a1-py2.py3-none-any.whl
- Upload date:
- Size: 9.3 kB
- Tags: Python 2, Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a74c3d7503e29b24f2183dd2037cea493c7401a36b8ad57dcc26691ef9b8a1e3
|
|
| MD5 |
3ea6d68896963dcbc60baaf9db02ef43
|
|
| BLAKE2b-256 |
2923a09a08eeb9e63258f75fa0c884f99df1093575bd121981f30d0e185214bc
|