Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@missionsquad/rosetta-ai",
"version": "1.11.4",
"version": "1.12.0",
"description": "Unified TypeScript SDK for interacting with multiple AI providers (Anthropic, Google, Groq, OpenAI).",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
Expand Down
6 changes: 6 additions & 0 deletions src/core/mapping/common.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ export function mapBaseParams(
temperature?: number
topP?: number
maxTokens?: number
reasoningEffort?: GenerateParams['reasoningEffort']
verbosity?: GenerateParams['verbosity']
stopSequences?: string[]
} {
const stopSequences = Array.isArray(params.stop) ? params.stop : params.stop ? [params.stop] : undefined
Expand All @@ -102,12 +104,16 @@ export function mapBaseParams(
temperature?: number
topP?: number
maxTokens?: number
reasoningEffort?: GenerateParams['reasoningEffort']
verbosity?: GenerateParams['verbosity']
stopSequences?: string[]
} = {}

if (params.temperature !== undefined) result.temperature = params.temperature
if (params.topP !== undefined) result.topP = params.topP
if (effectiveMaxTokens !== undefined) result.maxTokens = effectiveMaxTokens
if (params.reasoningEffort !== undefined) result.reasoningEffort = params.reasoningEffort
if (params.verbosity !== undefined) result.verbosity = params.verbosity
if (stopSequences !== undefined) result.stopSequences = stopSequences

return result
Expand Down
164 changes: 164 additions & 0 deletions src/core/mapping/gpt5.support.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import type { ReasoningEffort } from '../../types'

export interface Gpt5Support {
chatCompletionsSupported: boolean
allowedReasoningEfforts: ReasoningEffort[]
defaultReasoningEffort?: ReasoningEffort
fixedReasoningEffort?: ReasoningEffort
supportsVerbosity: boolean
supportsSampling: 'never' | 'always' | 'only_with_reasoning_none'
}

// Chat Completions-supported GPT-5 models verified for this implementation.
const GPT5_CHAT_COMPLETIONS_SUPPORT: Record<string, Gpt5Support> = {
'gpt-5': {
chatCompletionsSupported: true,
allowedReasoningEfforts: ['minimal', 'low', 'medium', 'high'],
defaultReasoningEffort: 'medium',
supportsVerbosity: true,
supportsSampling: 'never'
},
'gpt-5-mini': {
chatCompletionsSupported: true,
allowedReasoningEfforts: ['minimal', 'low', 'medium', 'high'],
defaultReasoningEffort: 'medium',
supportsVerbosity: true,
supportsSampling: 'never'
},
'gpt-5-nano': {
chatCompletionsSupported: true,
allowedReasoningEfforts: ['minimal', 'low', 'medium', 'high'],
defaultReasoningEffort: 'medium',
supportsVerbosity: true,
supportsSampling: 'never'
},
'gpt-5.1': {
chatCompletionsSupported: true,
allowedReasoningEfforts: ['none', 'low', 'medium', 'high'],
defaultReasoningEffort: 'none',
supportsVerbosity: true,
supportsSampling: 'never'
},
'gpt-5.2': {
chatCompletionsSupported: true,
allowedReasoningEfforts: ['none', 'low', 'medium', 'high', 'xhigh'],
defaultReasoningEffort: 'none',
supportsVerbosity: true,
supportsSampling: 'only_with_reasoning_none'
},
'gpt-5.4': {
chatCompletionsSupported: true,
allowedReasoningEfforts: ['none', 'low', 'medium', 'high', 'xhigh'],
defaultReasoningEffort: 'none',
supportsVerbosity: true,
supportsSampling: 'never'
},
'gpt-5-chat-latest': {
chatCompletionsSupported: true,
allowedReasoningEfforts: [],
supportsVerbosity: true,
supportsSampling: 'always'
},
'gpt-5.1-chat-latest': {
chatCompletionsSupported: true,
allowedReasoningEfforts: ['medium'],
fixedReasoningEffort: 'medium',
supportsVerbosity: true,
supportsSampling: 'never'
},
'gpt-5.2-chat-latest': {
chatCompletionsSupported: true,
allowedReasoningEfforts: ['medium'],
fixedReasoningEffort: 'medium',
supportsVerbosity: true,
supportsSampling: 'never'
}
}

// Verified GPT-5 inventory that is treated as Responses-only / unsupported on Chat Completions here.
const GPT5_CHAT_COMPLETIONS_UNSUPPORTED: Record<string, Gpt5Support> = {
'gpt-5-codex': {
chatCompletionsSupported: false,
allowedReasoningEfforts: [],
supportsVerbosity: false,
supportsSampling: 'never'
},
'gpt-5-pro': {
chatCompletionsSupported: false,
allowedReasoningEfforts: ['high'],
fixedReasoningEffort: 'high',
supportsVerbosity: true,
supportsSampling: 'never'
},
'gpt-5-search-api': {
chatCompletionsSupported: false,
allowedReasoningEfforts: [],
supportsVerbosity: false,
supportsSampling: 'never'
},
'gpt-5.1-codex': {
chatCompletionsSupported: false,
allowedReasoningEfforts: [],
supportsVerbosity: false,
supportsSampling: 'never'
},
'gpt-5.1-codex-max': {
chatCompletionsSupported: false,
allowedReasoningEfforts: [],
supportsVerbosity: false,
supportsSampling: 'never'
},
'gpt-5.1-codex-mini': {
chatCompletionsSupported: false,
allowedReasoningEfforts: [],
supportsVerbosity: false,
supportsSampling: 'never'
},
'gpt-5.2-codex': {
chatCompletionsSupported: false,
allowedReasoningEfforts: [],
supportsVerbosity: false,
supportsSampling: 'never'
},
'gpt-5.2-pro': {
chatCompletionsSupported: false,
allowedReasoningEfforts: ['high'],
fixedReasoningEffort: 'high',
supportsVerbosity: true,
supportsSampling: 'never'
},
'gpt-5.3-chat-latest': {
chatCompletionsSupported: false,
allowedReasoningEfforts: [],
supportsVerbosity: false,
supportsSampling: 'never'
},
'gpt-5.3-codex': {
chatCompletionsSupported: false,
allowedReasoningEfforts: [],
supportsVerbosity: false,
supportsSampling: 'never'
},
'gpt-5.4-pro': {
chatCompletionsSupported: false,
allowedReasoningEfforts: ['high'],
fixedReasoningEffort: 'high',
supportsVerbosity: true,
supportsSampling: 'never'
}
}

export const GPT5_SUPPORT_TABLE: Record<string, Gpt5Support> = {
...GPT5_CHAT_COMPLETIONS_SUPPORT,
...GPT5_CHAT_COMPLETIONS_UNSUPPORTED
}

const GPT5_SUPPORT_KEYS = Object.keys(GPT5_SUPPORT_TABLE).sort((a, b) => b.length - a.length)

export function getGpt5Support(model: string): Gpt5Support | undefined {
const exact = GPT5_SUPPORT_TABLE[model]
if (exact) return exact

const prefix = GPT5_SUPPORT_KEYS.find(key => model.startsWith(`${key}-`))
return prefix ? GPT5_SUPPORT_TABLE[prefix] : undefined
}
61 changes: 59 additions & 2 deletions src/core/mapping/openai.mapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { IProviderMapper } from './base.mapper'
import { mapBaseParams, mapBaseToolChoice, mapToOpenAIResponseFormat } from './common.utils'
import * as OpenAIEmbedMapper from './openai.embed.mapper'
import * as OpenAIAudioMapper from './openai.audio.mapper'
import { getGpt5Support } from './gpt5.support'
import {
isGPT5Model,
isThinkingModel,
Expand Down Expand Up @@ -209,9 +210,65 @@ export class OpenAIMapper implements IProviderMapper {
} else {
delete basePayload.max_tokens
}

if (isGPT5) {
if (basePayload.temperature != null && !isNaN(basePayload.temperature)) {
basePayload.temperature = 1
const support = getGpt5Support(params.model!)

if (support?.chatCompletionsSupported === false) {
throw new UnsupportedFeatureError(this.provider, `Chat Completions for model '${params.model!}'`)
}

if (support) {
const requestedEffort = baseMappedParams.reasoningEffort
let effectiveEffort: GenerateParams['reasoningEffort'] | undefined

if (support.fixedReasoningEffort) {
effectiveEffort = support.fixedReasoningEffort
} else if (support.allowedReasoningEfforts.length > 0) {
if (
requestedEffort !== undefined &&
support.allowedReasoningEfforts.includes(requestedEffort)
) {
effectiveEffort = requestedEffort
} else {
effectiveEffort = support.defaultReasoningEffort
}
}

if (effectiveEffort !== undefined) {
basePayload.reasoning_effort = effectiveEffort
} else {
delete basePayload.reasoning_effort
}

if (
support.supportsVerbosity &&
baseMappedParams.verbosity !== undefined &&
['low', 'medium', 'high'].includes(baseMappedParams.verbosity)
) {
basePayload.verbosity = baseMappedParams.verbosity
} else {
delete basePayload.verbosity
}

const allowsSampling =
support.supportsSampling === 'always' ||
(support.supportsSampling === 'only_with_reasoning_none' && effectiveEffort === 'none')

if (!allowsSampling) {
delete basePayload.temperature
delete basePayload.top_p
}

delete basePayload.max_tokens
} else {
// Conservative fallback for unknown future GPT-5-family models on Chat Completions:
// preserve token limit behavior but drop sampling and GPT-5-specific fields until modeled.
delete basePayload.temperature
delete basePayload.top_p
delete basePayload.reasoning_effort
delete basePayload.verbosity
delete basePayload.max_tokens
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/core/rosetta-ai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,10 @@ export class RosettaAI {
// --- Map Parameters ---
const mappedParams = mapper.mapToProviderParams ? mapper.mapToProviderParams(effectiveParams) : effectiveParams

if (process.env.DEBUG === 'true' && providerKey === Provider.Anthropic) {
console.log(`[ROSETTA DEBUG] Mapped Anthropic stream request:\n${JSON.stringify(mappedParams, null, 2)}`)
}
Comment on lines +730 to +732

// --- Execute ---
if (isCustom && mapper.executeStream && customConfig) {
// Custom Provider Execution Path
Expand Down
7 changes: 7 additions & 0 deletions src/types/params.types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { ProviderKey, RosettaMessage, RosettaTool, RosettaAudioData, GenerateParamsProviderState } from './common.types'
import { ProviderOptions } from './config.types'

export type ReasoningEffort = 'none' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh'
export type Verbosity = 'low' | 'medium' | 'high'

export type RosettaResponseFormat =
| { type: 'text' }
| {
Expand Down Expand Up @@ -44,6 +47,10 @@ export interface GenerateParams {
temperature?: number
/** Nucleus sampling parameter: considers only tokens comprising the top `topP` probability mass. */
topP?: number
/** GPT-5 reasoning effort control for supported OpenAI Chat Completions models. */
reasoningEffort?: ReasoningEffort
/** GPT-5 verbosity control for supported OpenAI Chat Completions models. */
verbosity?: Verbosity
/** Sequence(s) where the API will stop generating further tokens. */
stop?: string | string[] | null
/**
Expand Down
Loading
Loading