Call QuickSilver Pro from the OpenAI SDK, LangChain and the Vercel AI SDK

Working examples for the OpenAI Python and Node SDKs, LangChain's ChatOpenAI and the Vercel AI SDK against QuickSilver Pro — plain calls, streaming and tool calling, with the package versions we ran them on.

QuickSilver Pro speaks the OpenAI Chat Completions API, so any library built on it can call every model in the catalog — Claude, DeepSeek, Qwen, GPT and the rest — once you change two values: the base URL and the API key.

  • Base URL: https://api.quicksilverpro.io/v1
  • API key: a QuickSilver Pro key from the dashboard. The examples below read it from the QSP_API_KEY environment variable.

Every snippet on this page was run on 2026-10-20 with Claude Haiku 5.5 ($0.025 / $0.125 per million tokens) and, where noted, DeepSeek V4.1 Flash ($0.125 / $0.55). Both are cheap enough to experiment with; swap in any other model ID from the model list. New accounts get $0.05 of free credit, which covers the starter models (GPT-6 Luna, Claude Haiku 5.5, DeepSeek V4.1 Flash, Qwen3.8 Flash Next and MiMo-V2.6-Flash).

These are the package versions we used:

Library Package Version
OpenAI Python SDK openai 3.35.1 (Python 3.12)
OpenAI Node SDK openai 7.39.0 (run with Bun 1.3)
LangChain langchain-openai / langchain-core 1.7.2 / 1.6.12
Vercel AI SDK ai + @ai-sdk/openai-compatible 7.0.146 + 3.0.74

OpenAI Python SDK

pip install openai
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.quicksilverpro.io/v1",
    api_key=os.environ["QSP_API_KEY"],
)

response = client.chat.completions.create(
    model="claude-haiku-5-5",
    messages=[{"role": "user", "content": "Name the capital of France in one word."}],
    max_tokens=50,
)
print(response.choices[0].message.content)  # Paris

Streaming works the same way as with OpenAI. Add stream_options={"include_usage": True} if you want token counts in the last chunk:

stream = client.chat.completions.create(
    model="claude-haiku-5-5",
    messages=[{"role": "user", "content": "Count from 1 to 5, one number per line."}],
    max_tokens=100,
    stream=True,
    stream_options={"include_usage": True},
)
for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)
    if chunk.usage:
        print("\n", chunk.usage)

Tool calling uses the standard tools parameter. The model asks for a call, you run the function, and you send the result back as a tool message:

import json

tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Get the current temperature for a city.",
        "parameters": {
            "type": "object",
            "properties": {"city": {"type": "string"}},
            "required": ["city"],
        },
    },
}]

messages = [{"role": "user", "content": "What's the weather in Paris?"}]
first = client.chat.completions.create(
    model="claude-haiku-5-5", messages=messages, tools=tools, max_tokens=300,
)
call = first.choices[0].message.tool_calls[0]
print(call.function.name, call.function.arguments)  # get_weather {"city": "Paris"}

messages.append(first.choices[0].message)
messages.append({
    "role": "tool",
    "tool_call_id": call.id,
    "content": json.dumps({"city": "Paris", "temp_c": 18}),
})
final = client.chat.completions.create(
    model="claude-haiku-5-5", messages=messages, tools=tools, max_tokens=300,
)
print(final.choices[0].message.content)  # The current temperature in Paris is 18°C.

More on tools, including strict: true, is in the tools guide.

OpenAI Node SDK

npm install openai
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://api.quicksilverpro.io/v1",
  apiKey: process.env.QSP_API_KEY,
});

const response = await client.chat.completions.create({
  model: "claude-haiku-5-5",
  messages: [{ role: "user", content: "Name the capital of France in one word." }],
  max_tokens: 50,
});
console.log(response.choices[0].message.content);

Streaming:

const stream = await client.chat.completions.create({
  model: "claude-haiku-5-5",
  messages: [{ role: "user", content: "Count from 1 to 5, one number per line." }],
  max_tokens: 100,
  stream: true,
});
for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}

LangChain (Python)

Use ChatOpenAI from langchain-openai and pass the base URL and key:

pip install langchain-openai
import os
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
    model="claude-haiku-5-5",
    base_url="https://api.quicksilverpro.io/v1",
    api_key=os.environ["QSP_API_KEY"],
    max_tokens=100,
)

print(llm.invoke("Name the capital of France in one word.").content)

for chunk in llm.stream("Count from 1 to 5, one number per line."):
    print(chunk.content, end="", flush=True)

The same code also ran with model="deepseek-v4.1-flash" (with a larger max_tokens; see the note on reasoning models below).

Tools bind as usual:

from langchain_core.tools import tool

@tool
def get_weather(city: str) -> dict:
    """Get the current temperature for a city."""
    return {"city": city, "temp_c": 18}

msg = llm.bind_tools([get_weather]).invoke("What's the weather in Paris?")
print(msg.tool_calls)
# [{'name': 'get_weather', 'args': {'city': 'Paris'}, 'id': 'toolu_...', 'type': 'tool_call'}]

Structured output on Claude models needs method="function_calling". In this version of langchain-openai, with_structured_output defaults to OpenAI's json_schema response format. Claude models on QuickSilver Pro do not support that mode (see JSON schema): the model answered in prose, kept going until it hit max_tokens, and LangChain raised

openai.LengthFinishReasonError: Could not parse response content as the length limit was reached

Asking LangChain to use a tool call instead works:

from pydantic import BaseModel

class City(BaseModel):
    name: str
    country: str
    population_millions: float

structured = llm.with_structured_output(City, method="function_calling")
print(structured.invoke("Give me facts about Paris."))
# name='Paris' country='France' population_millions=2.1

DeepSeek V4.1 Flash does support json_schema; a direct request with a strict schema returned valid JSON.

Vercel AI SDK (TypeScript)

Use the @ai-sdk/openai-compatible provider:

npm install ai @ai-sdk/openai-compatible zod
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
import { generateText, streamText } from "ai";

const qsp = createOpenAICompatible({
  name: "quicksilverpro",
  baseURL: "https://api.quicksilverpro.io/v1",
  apiKey: process.env.QSP_API_KEY,
});

const { text } = await generateText({
  model: qsp("claude-haiku-5-5"),
  prompt: "Name the capital of France in one word.",
  maxOutputTokens: 50,
});
console.log(text);

const result = streamText({
  model: qsp("claude-haiku-5-5"),
  prompt: "Count from 1 to 5, one number per line.",
  maxOutputTokens: 100,
});
for await (const delta of result.textStream) process.stdout.write(delta);

Tool calling with automatic execution — the SDK runs execute, sends the result back and returns the final answer:

import { generateText, stepCountIs, tool } from "ai";
import { z } from "zod";

const { text, steps } = await generateText({
  model: qsp("claude-haiku-5-5"),
  prompt: "What's the weather in Paris?",
  tools: {
    getWeather: tool({
      description: "Get the current temperature for a city.",
      inputSchema: z.object({ city: z.string() }),
      execute: async ({ city }) => ({ city, tempC: 18 }),
    }),
  },
  stopWhen: stepCountIs(3),
  maxOutputTokens: 300,
});
console.log(text); // The current temperature in Paris is 18°C.

Why not @ai-sdk/openai? Its createOpenAI(...)("model") calls OpenAI's newer Responses API (/v1/responses) by default, not Chat Completions. With Claude Haiku 5.5 that failed on 2026-10-20: the SDK rejected the reply with AI_APICallError: Invalid JSON response, because a field it expects (annotations on the output text) was missing. The same call with DeepSeek V4.1 Flash worked. If you already use @ai-sdk/openai, call .chat() to stay on Chat Completions, which worked with both models:

import { createOpenAI } from "@ai-sdk/openai";

const qsp = createOpenAI({
  baseURL: "https://api.quicksilverpro.io/v1",
  apiKey: process.env.QSP_API_KEY,
});
const model = qsp.chat("claude-haiku-5-5");

Things to know

  • Reasoning models spend tokens before they answer. DeepSeek V4.1 Flash thinks first. With max_tokens=20, the whole budget went to reasoning: content came back null with finish_reason: "length", and the reasoning text was in reasoning_content. Give reasoning models a few hundred tokens or more; reasoning tokens are counted in completion_tokens.
  • Some OpenAI parameters are accepted but have no effect. On both Claude Haiku 5.5 and DeepSeek V4.1 Flash, n=2 returned one choice and logprobs=True returned no log probabilities — without an error. temperature, top_p, stop, seed, presence_penalty, user and max_completion_tokens were all accepted.
  • Strict JSON on Claude: use a tool. In our test Claude Haiku 5.5 accepted response_format: json_schema without an error and then ignored it, answering in prose. Define a tool with strict: true instead, as in the JSON schema guide.
  • Errors map to the SDK's usual exceptions. An unknown model ID raised BadRequestError (400) in the Python SDK and a wrong key raised AuthenticationError (401). The codes are listed in errors, and limits in rate limits.

For the Anthropic SDK and Claude Code, which use the base URL https://api.quicksilverpro.io without /v1, see Claude API pricing and Claude Code with QuickSilver Pro.