Skip to content
All docs

Code

OpenAI Agents SDK

The Agents SDK runs on nokeep through chat completions. Give it an AsyncOpenAI client with nokeep’s base URL and wrap the model in OpenAIChatCompletionsModel.

Checked 29 October 2026

Install and connect

Shell
pip install openai-agents
export NOKEEP_API_KEY="sk-nokeep-test"  # your own key once it connects
agent.py
import os
from openai import AsyncOpenAI
from agents import Agent, OpenAIChatCompletionsModel, set_tracing_disabled

client = AsyncOpenAI(
    base_url="https://nokeep.ai/v1",
    api_key=os.environ["NOKEEP_API_KEY"],
)
set_tracing_disabled(True)  # traces go to OpenAI's dashboard, which a nokeep key can't reach

model = OpenAIChatCompletionsModel(model="deepseek-v4.1-flash-uncensored", openai_client=client)

from agents import Runner

agent = Agent(name="Editor", instructions="You are a blunt editor.", model=model)
result = Runner.run_sync(agent, "Cut this opening line to ten words: ...")
print(result.final_output)

The SDK calls the Responses API (/v1/responses) unless told otherwise, and nokeep has only chat completions. Wrapping the model in OpenAIChatCompletionsModel is what makes it work.

Or set it once for every agent

Python
import os
from openai import AsyncOpenAI
from agents import Agent, set_default_openai_api, set_default_openai_client, set_tracing_disabled

set_default_openai_client(AsyncOpenAI(base_url="https://nokeep.ai/v1", api_key=os.environ["NOKEEP_API_KEY"]), use_for_tracing=False)
set_default_openai_api("chat_completions")
set_tracing_disabled(True)

agent = Agent(name="Editor", instructions="You are a blunt editor.", model="deepseek-v4.1-flash-uncensored")

Agents then take the model id as a plain string. Handoffs between agents work the same way.

Tools

Python
from agents import Agent, Runner, function_tool

@function_tool
def get_weather(city: str) -> str:
    """Get the weather in a city."""
    return f"18°C and clear in {city}"

agent = Agent(
    name="Weather",
    instructions="Answer questions about the weather.",
    model=model,
    tools=[get_weather],
)
print(Runner.run_sync(agent, "What's the weather in Lisbon?").final_output)

The SDK sends each tool with strict: true and runs the loop: the model asks for a tool, the SDK runs it and sends the result back until the model answers. See Tool calls.

Stream and usage

Python
import asyncio
from agents import Runner
from openai.types.responses import ResponseTextDeltaEvent

async def main():
    result = Runner.run_streamed(agent, "Tell me a story in three lines.")
    async for event in result.stream_events():
        if event.type == "raw_response_event" and isinstance(event.data, ResponseTextDeltaEvent):
            print(event.data.delta, end="", flush=True)
    print("\n", result.context_wrapper.usage)

asyncio.run(main())

The SDK asks for usage on a stream, so context_wrapper.usage holds the token counts for the whole run, every model call added up.

Reasoning effort and other settings

Python
from agents import Agent, ModelSettings
from openai.types.shared import Reasoning

planner = Agent(
    name="Planner",
    instructions="Plan before you answer.",
    model=model,
    model_settings=ModelSettings(
        reasoning=Reasoning(effort="high"),  # sent as reasoning_effort
        max_tokens=4000,
        extra_body={"min_p": 0.05},  # fields ModelSettings has no argument for
    ),
)

Thinking counts toward max_tokens, so give a thinking agent room. See Thinking.

Gotchas

  • `reasoning_effort` takes only the model’s own values. deepseek-v4.1-flash-uncensored takes none, low, high, max; glm-5.3-flash-uncensored takes low, high. Other apps’ minimal, medium and xhigh run at the model’s nearest level (medium as high, xhigh as max where the model has it); any other value gets 400 invalid_reasoning_effort. See Thinking.
  • GLM picks its own tools. glm-5.3-flash-uncensored takes tool_choice auto or none only. Forcing a tool (required, or naming a function) gets 400 unsupported_tool_choice. Use DeepSeek when you need to force one. In the SDK that is ModelSettings(tool_choice="required"), or a tool name.
  • Fields a model can’t take are dropped, not refused. The call still answers, and the x-nokeep-ignored response header names what was left out. GLM keeps only tools, tool_choice and reasoning_effort, so its temperature and response_format are dropped. See which fields each model takes.
  • Chat completions only. There is no /v1/responses and no /v1/embeddings; a framework that defaults to either needs to be pointed at chat completions, and embeddings need another provider.

Hosted tools, such as web search, file search and code interpreter, run on OpenAI’s own servers through the Responses API. They don’t work with any other API, nokeep included. Write them as function tools.

Questions

Do I need an OpenAI key as well?

No. Every model call goes to nokeep with your nokeep key. Tracing uploads to OpenAI’s dashboard; with tracing on and no OpenAI key set, the SDK prints OPENAI_API_KEY is not set, skipping trace export and the agent runs anyway. set_tracing_disabled(True) keeps it quiet.

Which model should an agent use?

deepseek-v4.1-flash-uncensored: it can be made to call a tool, reads pictures and takes every setting. glm-5.3-flash-uncensored costs less and works for agents that let the model choose its tools.