@tanstack/ai-byteplus calls Seed chat models with reasoning, plus Seedance video, Seedream images, and Seed Speech.
npm i @tanstack/ai-bytepluspnpm add @tanstack/ai-byteplusyarn add @tanstack/ai-byteplusbun add @tanstack/ai-byteplusBytePlus sells these models as two products, and each product has its own key. Put both keys in the environment of your server:
# ModelArk: chat, Seedance video, Seedream images
ARK_API_KEY=...
# Seed Speech: text-to-speech and transcription
BYTEPLUS_VOICE_API_KEY=...To pass the key in code, use the create* factory of the same adapter:
import { createBytePlusText } from "@tanstack/ai-byteplus";
const adapter = createBytePlusText("dola-seed-2-1-turbo-260628", process.env.ARK_API_KEY!);Ark keys work in one region only. The default endpoint is Asia-Pacific south-east. For a key from another region, see Regions.
import {
chat,
chatParamsFromRequest,
toServerSentEventsResponse,
} from "@tanstack/ai";
import { byteplusText } from "@tanstack/ai-byteplus";
export async function POST(request: Request) {
const { messages, threadId, runId } = await chatParamsFromRequest(request);
const stream = chat({
adapter: byteplusText("dola-seed-2-1-turbo-260628"),
messages,
threadId,
runId,
});
return toServerSentEventsResponse(stream);
}The client is the same for every provider. Point useChat at your route:
import { useState } from "react";
import { useChat, fetchServerSentEvents } from "@tanstack/ai-react";
export function Chat() {
const [input, setInput] = useState("");
const { messages, sendMessage } = useChat({
connection: fetchServerSentEvents("/api/chat"),
threadId: "support-chat",
});
return (
<form
onSubmit={(event) => {
event.preventDefault();
sendMessage(input);
setInput("");
}}
>
{messages.map((message) => (
<p key={message.id}>
{message.parts
.map((part) => (part.type === "text" ? part.content : ""))
.join("")}
</p>
))}
<input value={input} onChange={(event) => setInput(event.target.value)} />
</form>
);
}For Vue, Solid, Svelte, Angular, and the other frameworks, see Quick Start.
You now have a streaming chat on BytePlus. Seed models think before they answer, and useChat shows that thinking as separate thinking parts. Tools work the same as with every adapter. BytePlus has no provider tools of its own.
Video generation is an experimental feature.
Seedance runs as a job. generateVideo() starts the job and returns a jobId. Poll the job until it has a video URL:
import { generateVideo, getVideoJobStatus } from "@tanstack/ai";
import { byteplusVideo } from "@tanstack/ai-byteplus";
const adapter = byteplusVideo("dreamina-seedance-2-5-260628");
const { jobId } = await generateVideo({
adapter,
prompt: "a guitar being played in a store",
threadId: "thread-1",
size: "16:9_720p",
duration: 5,
});
let status = await getVideoJobStatus({ adapter, jobId });
while (status.status === "pending" || status.status === "processing") {
await new Promise((resolve) => setTimeout(resolve, 5000));
status = await getVideoJobStatus({ adapter, jobId });
}
console.log(status.status === "completed" ? status.url : status.error);To run the job from a React component, and for the options of each model, see BytePlus: Advanced.
import { generateImage } from "@tanstack/ai";
import { byteplusImage } from "@tanstack/ai-byteplus";
const result = await generateImage({
adapter: byteplusImage("dola-seedream-5-0-pro-260628"),
prompt: "a guitar being played in a store",
threadId: "thread-1",
size: "2K",
modelOptions: { watermark: false },
});
console.log(result.images[0]?.url);To edit an existing image, see Seedream reference images.
This adapter uses BYTEPLUS_VOICE_API_KEY:
import { generateSpeech } from "@tanstack/ai";
import { byteplusSpeech } from "@tanstack/ai-byteplus";
const result = await generateSpeech({
adapter: byteplusSpeech("seed-audio-1.0"),
text: "welcome to the guitar store",
threadId: "thread-1",
voice: "en_female_stokie_uranus_bigtts",
format: "mp3",
});
console.log(result.contentType, result.audio.length);The voice ids, voice cloning, timings, dialogue, and watermarks are in Seed Speech. The client half is the standard useGenerateSpeech hook.
This adapter also uses BYTEPLUS_VOICE_API_KEY. The call is synchronous, so you get the transcript back with no polling:
import { generateTranscription } from "@tanstack/ai";
import { byteplusTranscription } from "@tanstack/ai-byteplus";
const result = await generateTranscription({
adapter: byteplusTranscription("seed-asr"),
audio: "https://example.com/recording.mp3",
modelOptions: { enable_punc: true, enable_speaker_info: true },
threadId: "thread-1",
});
console.log(result.text);
for (const segment of result.segments ?? []) {
console.log(segment.speaker, segment.text);
}audio takes a File, base64, a data URL, or a public URL. The limit is two hours and 100 MB. For the other options, see Transcription options.
Ark speaks the OpenAI chat format, so the sampling options keep their OpenAI names. They go in modelOptions:
import { chat } from "@tanstack/ai";
import { byteplusText } from "@tanstack/ai-byteplus";
const stream = chat({
adapter: byteplusText("dola-seed-2-1-turbo-260628"),
messages: [{ role: "user", content: "Hello!" }],
threadId: "thread-1",
modelOptions: {
temperature: 0.7,
top_p: 0.9,
max_tokens: 2048,
},
});Need a header on each request, or a log of each model call? The chat adapter supports wrapFetch.
Seed models think by default. Set how hard they think with reasoning on chat():
import { chat } from "@tanstack/ai";
import { byteplusText } from "@tanstack/ai-byteplus";
const stream = chat({
adapter: byteplusText("glm-5-2-260617"),
messages: [{ role: "user", content: "Hello!" }],
threadId: "thread-1",
reasoning: "off",
});The levels of each model:
What goes over the wire, and how the adapter keeps the encrypted_content of the reasoning, is in Reasoning and encrypted_content.
Ten of the 18 chat models support chat({ outputSchema }). Use dola-seed-2-1-turbo-260628 or seed-2-0-lite-260228. On the other models, the call fails with an error that names the models that work. See Structured output.
Newest first. The package exports the full lists: BYTEPLUS_CHAT_MODELS, BYTEPLUS_VIDEO_MODELS, BYTEPLUS_IMAGE_MODELS, BYTEPLUS_TTS_MODELS, and BYTEPLUS_TRANSCRIPTION_MODELS.
| Activity | Factory | Models |
|---|---|---|
| Chat | byteplusText | dola-seed-2-1-turbo-260628, seed-2-0-lite-260428, seed-2-0-mini-260428, seed-2-0-pro-260328, seed-2-0-lite-260228, seed-2-0-mini-260215, seed-2-0-code-preview-260328, seed-1-8-251228, seed-1-6-250915, seed-1-6-250615, seed-1-6-flash-250715, seed-1-6-flash-250615, glm-5-2-260617, glm-4-7-251222, deepseek-v4-pro-260425, deepseek-v4-flash-260425, deepseek-v3-2-251201, gpt-oss-120b-250805 |
| Video | byteplusVideo | dreamina-seedance-2-5-260628, dreamina-seedance-2-0-260128, dreamina-seedance-2-0-fast-260128, dreamina-seedance-2-0-mini-260615, seedance-1-5-pro-251215, seedance-1-0-pro-250528, seedance-1-0-pro-fast-251015 |
| Images | byteplusImage | dola-seedream-5-0-pro-260628, seedream-5-0-260128, seedream-5-0-lite-260128, seedream-4-5-251128, seedream-4-0-250828 |
| Speech | byteplusSpeech | seed-audio-1.0 |
| Transcription | byteplusTranscription | seed-asr |
BytePlus retires model ids often, and its published lists contain ids that do not work. This package lists only dated ids that answered a live request.
adapter.inputModalities lists the input kinds of the selected model. See Check what a model accepts.
These topics are on BytePlus: Advanced: