Skip to main content
Pre-release

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

If you're not sure about the file name format, learn more about wheel file names.

File details

Details for the file chat_completions_conversation_with_tools-0.1.0a1.tar.gz.

File metadata

File hashes

Hashes for chat_completions_conversation_with_tools-0.1.0a1.tar.gz
Algorithm Hash digest
SHA256 45aab943751eeaf0cc044e31a327651a4d6a6bb369731829aa4ce9f627a87407
MD5 1fa34d7e2f2e4fe2c92c6362464c3dd7
BLAKE2b-256 eaff1baa9b7048e0230aefc883ce3e65bffb3657779c0187596cf86cc2a2da30

See more details on using hashes here.

File details

Details for the file chat_completions_conversation_with_tools-0.1.0a1-py2.py3-none-any.whl.

File metadata

File hashes

Hashes for chat_completions_conversation_with_tools-0.1.0a1-py2.py3-none-any.whl
Algorithm Hash digest
SHA256 a74c3d7503e29b24f2183dd2037cea493c7401a36b8ad57dcc26691ef9b8a1e3
MD5 3ea6d68896963dcbc60baaf9db02ef43
BLAKE2b-256 2923a09a08eeb9e63258f75fa0c884f99df1093575bd121981f30d0e185214bc

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page