Skip to content
All docs

Code

Vercel AI SDK

Make a nokeep provider with createOpenAICompatible from @ai-sdk/openai-compatible. Then generateText, streamText and tools work as with any AI SDK model.

Checked 29 October 2026

Install and set up the provider

Shell
npm install ai @ai-sdk/openai-compatible zod
export NOKEEP_API_KEY="sk-nokeep-test"  # your own key once it connects
nokeep.js
import { createOpenAICompatible } from "@ai-sdk/openai-compatible"

export const nokeep = createOpenAICompatible({
  name: "nokeep",
  apiKey: process.env.NOKEEP_API_KEY,
  baseURL: "https://nokeep.ai/v1",
  includeUsage: true, // token counts on streamed replies too
})

nokeep("<model id>") gives you a model. The ids are deepseek-v4.1-flash-uncensored and glm-5.3-flash-uncensored: compare them in Models, or read them from GET /v1/models. includeUsage: true asks for token counts on streamed replies; without it, streamText reports none.

generateText

JavaScript
import { generateText } from "ai"
import { nokeep } from "./nokeep.js"

const { text, usage } = await generateText({
  model: nokeep("deepseek-v4.1-flash-uncensored"),
  system: "You are a blunt editor.",
  prompt: "Cut this opening line to ten words: ...",
})
console.log(text)
console.log(usage.inputTokens, usage.outputTokens)

streamText

JavaScript
import { streamText } from "ai"
import { nokeep } from "./nokeep.js"

const result = streamText({
  model: nokeep("deepseek-v4.1-flash-uncensored"),
  prompt: "Tell me a story in three lines.",
})
for await (const text of result.textStream) process.stdout.write(text)

// With includeUsage on, the counts arrive once the stream ends.
console.log(await result.usage)

In a Next.js route handler, return result.toUIMessageStreamResponse() for useChat, or result.toTextStreamResponse() for plain text. Stop a reply by aborting the request (abortSignal); you pay for what was written before you stopped. See Streaming.

Tool calling

JavaScript
import { generateText, stepCountIs, tool } from "ai"
import { z } from "zod"
import { nokeep } from "./nokeep.js"

const { text } = await generateText({
  model: nokeep("deepseek-v4.1-flash-uncensored"),
  tools: {
    weather: tool({
      description: "Get the weather in a city",
      inputSchema: z.object({ city: z.string() }),
      execute: async ({ city }) => ({ city, celsius: 18 }),
    }),
  },
  stopWhen: stepCountIs(5), // let the model call the tool, read the result and answer
  prompt: "What's the weather in Lisbon?",
})
console.log(text)

Both models call tools. glm-5.3-flash-uncensored picks its own: toolChoice: "required" or a named tool gets a 400, so use DeepSeek when a tool must run. See Tool calls.

Reasoning effort

JavaScript
import { generateText } from "ai"
import { nokeep } from "./nokeep.js"

const { text, reasoningText } = await generateText({
  model: nokeep("deepseek-v4.1-flash-uncensored"),
  prompt: "Plan a three-act heist story in five bullet points.",
  maxOutputTokens: 4000, // thinking counts toward the limit
  providerOptions: { nokeep: { reasoningEffort: "high" } },
})
console.log(reasoningText) // the thinking, apart from the answer
console.log(text)

The key under providerOptions is the name you gave the provider. It is sent as reasoning_effort, unchanged. DeepSeek V4.1 Flash Uncensored answers at once unless you ask it to think: it takes none, low, high, max. The AI SDK’s own reasoning: "high" option sends the same field. See Thinking.

Pictures (DeepSeek)

JavaScript
import { readFile } from "node:fs/promises"
import { generateText } from "ai"
import { nokeep } from "./nokeep.js"

const { text } = await generateText({
  model: nokeep("deepseek-v4.1-flash-uncensored"),
  messages: [{
    role: "user",
    content: [
      { type: "text", text: "What is in this picture?" },
      { type: "file", mediaType: "image/jpeg", data: await readFile("photo.jpg") },
    ],
  }],
})
console.log(text)
  • deepseek-v4.1-flash-uncensored reads up to 10 pictures per request. glm-5.3-flash-uncensored reads text only and answers 400 unsupported_input.
  • The AI SDK sends every picture inside the request, as base64. Pass a URL as data and it downloads the file first. A request can be up to 12 MB, so shrink large photos.
  • Pictures are file parts with an image/* media type. The older image part still works but is deprecated.
  • Other files, such as PDFs or audio, are refused with 400 unsupported_input. See Pictures.

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.
  • 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.
  • `n` must be 1. Ask for several replies with several calls.
  • Use `@ai-sdk/openai-compatible`, not `@ai-sdk/openai`. The OpenAI provider calls /v1/responses by default, which nokeep doesn’t have.

Questions

Is there a nokeep provider package for the AI SDK?

You don’t need one. @ai-sdk/openai-compatible is the AI SDK’s own package for OpenAI-compatible APIs, and every feature on this page works through it.

Does streamText report usage?

Yes, when the provider is made with includeUsage: true. await result.usage then gives the token counts for the call.

Can I try it without paying?

Yes. Use the key sk-nokeep-test: every call answers with one fixed line and costs nothing, so you can wire up your app first. The free test key.