Show HN: Padwan-LLM, a lightweight LLM Python client A developer released Padwan-LLM, a lightweight async Python client that unifies access to OpenAI, Gemini, Mistral, Grok, Anthropic, and any OpenAI-compatible API through a single interface. The package ships with one runtime dependency, niquests, and negotiates HTTP/2 and HTTP/3 automatically, with a separate padwan-cli package providing the full interactive CLI/TUI. Padwan-LLM adds an AgentSession for multi-turn tool-calling conversations, built-in streamable-HTTP and stdio MCP transports, Gemini reasoning-token streaming via an on_thought callback, and a RealtimeClient for bidirectional voice sessions over WebSocket supporting OpenAI gpt-realtime, Gemini Live, and Grok Voice. Lightweight, unified async client for OpenAI, Gemini, Mistral, Grok, Anthropic, and any OpenAI-compatible API. Single runtime dependency niquests https://github.com/jawah/niquests , automatic HTTP/2 and HTTP/3 negotiation. For the full interactive CLI/TUI, use the separate padwan-cli https://github.com/polarsen-io/padwan-cli package. pip install padwan-llm python from padwan llm import LLMClient async with LLMClient model="gpt-4o" as client: response, usage = await client.complete chat {"role": "user", "content": "Hello "} print response "content" python from padwan llm import LLMClient, ConversationState state = ConversationState system="You are a concise assistant." async with LLMClient model="gpt-4o" as client: state.add user message "What's Python?" stream = client.stream chat state.messages chunks: list str = async for text in stream: print text, end="", flush=True chunks.append text state.add assistant message "".join chunks if stream.usage: state.accumulate usage stream.usage AgentSession drives a multi-turn conversation that can dispatch tool calls on each round, feed the results back, and repeat until the model returns a plain text answer. The mcp tools list accepts both individual McpTool instances and whole McpTransport servers — transports are entered as part of the session lifecycle: python from padwan llm import AgentSession, LLMClient, McpStdio async with AgentSession client=LLMClient model="gpt-4o" , mcp tools= McpStdio command="uvx", args= "my-mcp-server" , system="You have access to tools. Use them when helpful.", as session: async for chunk in session.stream "What's the weather in Paris?" : print chunk, end="", flush=True Or collect the full response in one call: text = await session.send "And in London?" AgentSession supports sequential or parallel tool execution, approval hooks, per-tool error handlers, and optional snapshot persistence via a ConversationStore protocol — see docs/agents.md https://github.com/polarsen-io/padwan-llm/blob/master/docs/agents.md . Both streamable-HTTP and stdio MCP transports are built in: python from padwan llm import McpStreamable, McpStdio Remote MCP server over HTTP with optional bearer token async with McpStreamable url="https://mcp.example.com/mcp", token="sk-..." as mcp: for tool in mcp.tools: print tool.name, tool.description Local subprocess async with McpStdio command="uvx", args= "my-mcp-server" as mcp: result = await mcp.tools 0 .handler {"query": "hello"} See docs/mcp.md https://github.com/polarsen-io/padwan-llm/blob/master/docs/mcp.md for the full feature matrix and architecture. Gemini's reasoning models can stream their internal thought tokens separately from the final answer. Wire an on thought callback to receive them: python from padwan llm import GeminiClient thoughts: list str = async with GeminiClient model="gemini-2.5-flash", on thought=thoughts.append, thinking config={"thinkingBudget": 2048, "includeThoughts": True}, as client: stream = client.stream chat {"role": "user", "content": "What is 7 8?"} async for chunk in stream: print chunk, end="" print "\n---\nReasoning:", "".join thoughts RealtimeClient opens a bidirectional voice session over a WebSocket and yields the live connection: stream microphone audio in, receive model audio and transcripts back. OpenAI gpt-realtime , Gemini Live, and Grok Voice are supported, dispatched by model name. Requires the realtime extra pip install "padwan-llm realtime " : python from padwan llm import RealtimeClient async with RealtimeClient instructions="Answer briefly.", voice="marin" as conn: await conn.append audio pcm16 chunk mono PCM16 microphone audio async for event in conn: if audio := conn.audio delta bytes event : playback.write audio Server-side VAD drives turn-taking by default; pass turn detection=NO TURN DETECTION for manual push-to-talk. See the realtime sections of docs/clients/openai.md https://github.com/polarsen-io/padwan-llm/blob/master/docs/clients/openai.md , docs/clients/gemini.md https://github.com/polarsen-io/padwan-llm/blob/master/docs/clients/gemini.md , and docs/clients/grok.md https://github.com/polarsen-io/padwan-llm/blob/master/docs/clients/grok.md . Opt-in GenAI spans and metrics for every provider client, following the OTel GenAI semantic conventions. Requires the otel extra pip install "padwan-llm otel " : python from padwan llm import otel otel.instrument uses the global tracer/meter providers For a managed trace backend, the Langfuse adapter configures both sides and maps Padwan chat, agent, tool, embedding, and MCP spans to Langfuse observations: pip install "padwan-llm langfuse " python from padwan llm.langfuse import instrument telemetry = instrument uses the standard LANGFUSE environment variables Chat calls emit a chat