샘플링 옵션을 modelOptions로 이동
요약: 이는 호환성이 깨지는 변경 사항입니다.
chat()/ai()/generate()의 최상위 편의 샘플링 prop인temperature,topP,maxTokens가 제거되었으며 이제 대신 provider-nativemodelOptions내부에 있습니다. 최상위에서 전달해도 더 이상 타입 검사를 통과하지 않으며 런타임에서 아무런 효과가 없습니다. 각 항목을 해당 provider의 표준 이름(예: OpenAI의max_output_tokens, Anthropic의max_tokens, Gemini의maxOutputTokens, Ollama의 중첩된options.num_predict)으로modelOptions안으로 이동해야 합니다. provider를 인식하는 codemod가 이 변환을 대신 수행합니다.metadata는 영향을 받지 않으며 최상위에 그대로 둡니다.
변경된 내용
이전에는 chat()이 세 가지 일반 샘플링 prop을 옵션의 최상위에서 직접 허용했습니다.
chat({
adapter: openaiText('gpt-4o'),
messages,
temperature: 0.3,
topP: 0.9,
maxTokens: 100,
})
이 prop은 런타임이 기본 provider가 기대하는 형식으로 매핑하는 편의 계층이었습니다. 이제 이러한 일반 매핑은 사라졌습니다. 샘플링 매개변수는 다른 모든 모델별 조정값이 이미 있는 곳, 즉 provider-native modelOptions 객체 내부에서 각 provider의 표준 키 이름으로 지정합니다.
import { chat } from '@tanstack/ai'
import { openaiText } from '@tanstack/ai-openai'
const messages = [{ role: 'user' as const, content: 'Hello' }]
chat({
adapter: openaiText('gpt-4o'),
messages,
modelOptions: {
temperature: 0.3,
top_p: 0.9,
max_output_tokens: 100,
},
})
변경 이유
- Provider-native, 단일 진실 공급원. 모든 provider는 이러한 매개변수의 이름을 다르게 지정합니다. OpenAI의 Responses API는
max_output_tokens를 사용하고, Anthropic은max_tokens, Gemini는maxOutputTokens를 사용하며, Ollama는options아래에 중첩합니다. 일반maxTokensprop 하나로는 provider마다 대상을 추측해야 했습니다. 이를modelOptions에 넣으면 샘플링이 존재하는 위치가 정확히 한 곳이 되고 provider 자체의 API 표면과 일치합니다. - 타입이 지정됩니다.
modelOptions는 이미 adapter+model별로 타입이 지정되어 있으므로 샘플링을 그곳으로 이동하면 느슨한 타입의 최상위 prop 세 개 대신 특정 모델이 허용하는 정확한 키에 대한 자동 완성과 컴파일 타임 검사를 사용할 수 있습니다. - 일반 매핑이 없습니다. 특히 추론 모델은 이러한 매개변수를 일관되게 처리하지 않습니다(
temperature를 무시하거나, thinking budget보다 낮은max_tokens를 거부하는 모델 등이 있습니다). 일반적인 최상위 매핑은 이러한 차이를 가리고 있었지만, provider-nativemodelOptions를 사용하면 각 adapter가 이를 정확하게 처리할 수 있습니다.
Provider별 이전 / 이후
최상위 prop 이름은 어디서나 동일합니다(temperature, topP, maxTokens). modelOptions의 대상 키는 provider마다 다르므로 provider가 기대하는 정확한 키를 사용해야 합니다.
OpenAI
// Before
chat({
adapter: openaiText('gpt-4o'),
messages,
temperature: 0.3,
topP: 0.9,
maxTokens: 100,
})
import { chat } from '@tanstack/ai'
import { openaiText } from '@tanstack/ai-openai'
const messages = [{ role: 'user' as const, content: 'Hello' }]
// After
chat({
adapter: openaiText('gpt-4o'),
messages,
modelOptions: {
temperature: 0.3,
top_p: 0.9,
max_output_tokens: 100,
},
})
Anthropic
// Before
chat({
adapter: anthropicText('claude-sonnet-4-5'),
messages,
temperature: 0.3,
topP: 0.9,
maxTokens: 1024,
})
import { chat } from '@tanstack/ai'
import { anthropicText } from '@tanstack/ai-anthropic'
const messages = [{ role: 'user' as const, content: 'Hello' }]
// After
chat({
adapter: anthropicText('claude-sonnet-4-5'),
messages,
modelOptions: {
temperature: 0.3,
top_p: 0.9,
max_tokens: 1024,
},
})
Gemini
// Before
chat({
adapter: geminiText('gemini-3.1-pro-preview'),
messages,
temperature: 0.3,
topP: 0.9,
maxTokens: 2048,
})
import { chat } from '@tanstack/ai'
import { geminiText } from '@tanstack/ai-gemini'
const messages = [{ role: 'user' as const, content: 'Hello' }]
// After
chat({
adapter: geminiText('gemini-3.1-pro-preview'),
messages,
modelOptions: {
temperature: 0.3,
topP: 0.9,
maxOutputTokens: 2048,
},
})
Ollama (options 아래에 중첩)
Ollama는 샘플링 매개변수가 modelOptions 내부의 options 객체 안에 중첩되는 유일한 provider이며, 토큰 제한 이름은 num_predict입니다.
// Before
chat({
adapter: ollamaText('llama3'),
messages,
temperature: 0.3,
topP: 0.9,
maxTokens: 1000,
})
import { chat } from '@tanstack/ai'
import { ollamaText } from '@tanstack/ai-ollama'
// After
chat({
adapter: ollamaText('llama3'),
messages,
modelOptions: {
options: {
temperature: 0.3,
top_p: 0.9,
num_predict: 1000,
},
},
})
프로바이더 키 참고표
| 최상위 prop | OpenAI | Anthropic | Gemini | Grok | Groq | OpenRouter | Ollama (options 아래에 중첩) |
|---|---|---|---|---|---|---|---|
temperature | temperature | temperature | temperature | temperature | temperature | temperature | options.temperature |
topP | top_p | top_p | topP | top_p | top_p | topP | options.top_p |
maxTokens | max_output_tokens | max_tokens | maxOutputTokens | max_tokens | max_completion_tokens | maxCompletionTokens | options.num_predict |
자동 codemod
jscodeshift codemod가 최상위 샘플링 prop을 modelOptions로 이동하고 각각을 올바른 provider-native 키로 이름을 변경합니다. adapter: factory 호출(예: openaiText('gpt-4o') → OpenAI)에서 provider를 확인하므로 변환이 provider를 인식합니다. 저장소에서 다음을 실행합니다.
pnpm codemod:move-sampling-to-model-options "src/**/*.{ts,tsx}"
또는 게시된 transform을 직접 실행할 수 있으며 clone이 필요하지 않습니다.
npx jscodeshift \
--parser=tsx \
-t https://raw.githubusercontent.com/TanStack/ai/main/codemods/move-sampling-to-model-options/transform.ts \
"src/**/*.{ts,tsx}"
--dry --print를 추가하면 파일을 수정하지 않고 변환 결과를 미리 확인할 수 있습니다.
수행하는 작업:
@tanstack/ai에서 가져온chat(),ai(),generate(),createChatOptions()호출을 대상으로 합니다.adapter:factory 호출에서 provider를 확인하고, 존재하는 각 최상위 prop의 이름을 해당 provider의 표준 키로 변경합니다.- Ollama의 경우 이름을 변경한 키를
modelOptions.options안에 중첩합니다. - 기존에
modelOptions객체 리터럴이 있으면 병합하고, 원래 값 표현식을 보존하며 축약 prop을 확장합니다({ temperature }→temperature: temperature).
보고 및 건너뛰기(부분 변환 안 함): codemod는 호출을 부분적으로 변환하지 않습니다. 안전하게 진행할 수 없으면 호출을 수정하지 않고 api.report(...) 메시지를 출력합니다.
- 확인할 수 없는 adapter —
adapterprop이 없거나, adapter가 인식된 provider factory 호출이 아니거나(예:makeAdapter()), 동적이거나 spread인 경우입니다. modelOptions가 일반 객체 리터럴이 아님 — 예를 들어 spread이거나 식별자 참조인 경우입니다.- 키 충돌 — 이름을 변경할 대상 키가 이미
modelOptions에 있거나 Ollama의 경우modelOptions.options에 있는 경우입니다. 이러한 충돌은 직접 해결해야 합니다.
전체 변환 세부 정보와 제한 사항은 codemods/move-sampling-to-model-options/README.md를 참조하세요.
최상위에 유지되는 항목
metadata는 샘플링 매개변수가 아니므로 영향을 받지 않으며 chat()의 최상위에 그대로 둡니다.
import { chat } from '@tanstack/ai'
import { openaiText } from '@tanstack/ai-openai'
const messages = [{ role: 'user' as const, content: 'Hello' }]
chat({
adapter: openaiText('gpt-4o'),
messages,
metadata: { requestId: 'abc-123' }, // ← still at the root
modelOptions: {
temperature: 0.3,
max_output_tokens: 100,
},
})