🧭 How toAIIntermediate✨ AI-assisted

How to Migrate From GPT-6 Sol to GPT-6.1 Sol

WittyTech Daily··5 min read
#gpt-6.1-sol#migration#responses-api#openai

Swapping gpt-6-sol for gpt-6.1-sol looks like a one-line diff until a request sets a reasoning effort the new model doesn't support. This guide shows how to migrate to GPT-6.1 Sol: the new effort range, tool calls on the Responses API, the parameters to delete, the price changes, and a side-by-side script to run before you cut over.

What changes when you migrate to GPT-6.1 Sol

| | gpt-6-sol | gpt-6.1-sol | | --- | --- | --- | | Reasoning effort | none, low, medium (default), high, xhigh, max | low, medium (default), high, xhigh, max | | Tool calling | Responses API; Chat Completions only at none | Responses API; Chat Completions only without tools | | Input / output per 1M tokens | $2 / $10 | $2 / $10 | | Cached input per 1M tokens | $0.20 | $0.10 |

Both model pages list a 1,050,000-token context window and 128,000 max output tokens. The details come from the GPT-6.1 Sol model page and OpenAI's latest model guide.

1. Find every call that will break

grep -rnE 'gpt-6-sol|reasoning_effort|"effort"|temperature|top_p|logprobs|chat.completions' src/

The hits are your checklist: model IDs, effort settings, sampling parameters, and Chat Completions calls that carry tools.

2. Swap the model ID and pick a reasoning effort

import os
from openai import OpenAI

client = OpenAI()

response = client.responses.create(
    model=os.environ.get("SOL_MODEL", "gpt-6.1-sol"),
    reasoning={"effort": "low"},  # was "none" or "minimal"
    input=[{"role": "user", "content": "Summarize this ticket in two sentences."}],
)
print(response.output_text)

none and minimal are not supported, so replace either with low. Move up only where your evals say you need to. If you omit the field, you get medium. On Chat Completions the same setting is reasoning_effort="low".

Everything in this guide also runs on gpt-6-sol, so setting SOL_MODEL=gpt-6-sol is your rollback.

3. Remove temperature and top_p

When reasoning is on, OpenAI says to remove temperature, top_p and top_logprobs, plus logprobs on Chat Completions. Since this model has no none, that means always.

SAMPLING_PARAMS = {"temperature", "top_p", "top_logprobs", "logprobs"}

def sol_61_params(params: dict) -> dict:
    return {k: v for k, v in params.items() if k not in SAMPLING_PARAMS}

If one wrapper builds requests for many callers, filter there during the migration, then delete the settings at the call sites. If you leaned on low temperature for repeatable output, tighten the prompt or use structured outputs, and measure consistency with evals.

4. Move tool calling to the Responses API

On gpt-6-sol, Chat Completions handled tools only at effort none. On gpt-6.1-sol, tools need the Responses API. A typical before:

resp = client.chat.completions.create(
    model="gpt-6-sol",
    reasoning_effort="none",
    messages=messages,
    tools=[{"type": "function", "function": {
        "name": "get_order_status",
        "description": "Look up an order by ID.",
        "parameters": {"type": "object", "properties": {"order_id": {"type": "string"}}, "required": ["order_id"]},
    }}],
)

And the after:

import json
from openai import OpenAI

client = OpenAI()

tools = [{
    "type": "function",
    "name": "get_order_status",
    "description": "Look up an order by ID.",
    "parameters": {"type": "object", "properties": {"order_id": {"type": "string"}}, "required": ["order_id"]},
}]

input_list = [{"role": "user", "content": "Where is order 1042?"}]
while True:
    response = client.responses.create(
        model="gpt-6.1-sol", reasoning={"effort": "low"}, tools=tools, input=input_list
    )
    input_list += response.output
    calls = [i for i in response.output if i.type == "function_call"]
    if not calls:
        break
    for call in calls:
        result = get_order_status(**json.loads(call.arguments))  # your function
        input_list.append({
            "type": "function_call_output",
            "call_id": call.call_id,
            "output": json.dumps(result),
        })
print(response.output_text)

The tool definition is now flat, with name at the top level instead of nested under function. input_list += response.output passes the model's reasoning items back along with your tool results, as the function calling guide recommends. With more than one tool, dispatch on call.name.

5. Re-check the bill

Input and output rates stay at $2 and $10 per 1M tokens. Cached input halves to $0.10, so a 100K-token cached prefix costs $0.01 per call instead of $0.02.

Three other rules apply:

  • Prompts over 272K input tokens are priced at 2x input and cache rates and 1.5x output.
  • Batch is 50% lower than standard pricing.
  • Fast mode is 2x standard pricing.

6. Run a side-by-side check before you cut over

Send the same cases to both models and compare tool calls, text, tokens and cost.

import json
import time
from openai import OpenAI

client = OpenAI()

CASES = json.load(open("eval_cases.json"))  # [{"id": "t1", "prompt": "..."}]
TOOLS = []  # your production tools, Responses format; drop tools= if you have none

# (input, cached input, output) USD per 1M tokens, from the model pages
RATES = {"gpt-6-sol": (2.0, 0.20, 10.0), "gpt-6.1-sol": (2.0, 0.10, 10.0)}
EFFORT = {"gpt-6-sol": "none", "gpt-6.1-sol": "low"}  # current vs new setting

def usd(model, u):
    r_in, r_cache, r_out = RATES[model]
    if u.input_tokens > 272_000:  # premium rates apply to the whole prompt
        r_in, r_cache, r_out = r_in * 2, r_cache * 2, r_out * 1.5
    cached = u.input_tokens_details.cached_tokens
    return ((u.input_tokens - cached) * r_in + cached * r_cache + u.output_tokens * r_out) / 1_000_000

for case in CASES:
    for model, effort in EFFORT.items():
        start = time.time()
        r = client.responses.create(
            model=model, reasoning={"effort": effort}, tools=TOOLS, input=case["prompt"]
        )
        print(json.dumps({
            "case": case["id"],
            "model": model,
            "seconds": round(time.time() - start, 2),
            "tool_calls": [(i.name, i.arguments) for i in r.output if i.type == "function_call"],
            "reasoning_tokens": r.usage.output_tokens_details.reasoning_tokens,
            "usd": round(usd(model, r.usage), 5),
            "text": r.output_text,
        }))

Run it with python compare.py > sol_compare.jsonl. Look for the same tool called with the same arguments, answers that hold up at low, and the shift in seconds and cost. Raise the effort one notch only for cases that regressed. Then ship behind the SOL_MODEL variable and move a slice of traffic first.

Gotchas

  • Going from none to low adds reasoning tokens, which bill as output tokens and add latency. The reasoning_tokens field shows how many.
  • The context window is 1,050,000 tokens, but max input is 922,000, and anything over 272K input tokens lands in the premium tier.
  • Rate limits are per model. The model page lists 500 to 15,000 RPM and 500,000 to 40,000,000 TPM across Tiers 1 to 5, so check yours before moving traffic.
  • The model page lists only gpt-6.1-sol as a snapshot, so there is no dated ID to pin. Re-run the comparison on a schedule.
  • This guide covers OpenAI's API. If you reach the model through a cloud provider, that provider's pricing and API surface apply.

Change the model ID, effort, sampling parameters and tool format together, prove it with the comparison run, and keep rollback to one environment variable.

A model swap changes your error rate, latency and bill in one deploy, so the side-by-side run is what makes it safe to ship.

← More in AI