@providerprotocol/ai 0.0.5 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/dist/anthropic/index.js +1 -24
  2. package/dist/anthropic/index.js.map +1 -1
  3. package/dist/google/index.js +3 -46
  4. package/dist/google/index.js.map +1 -1
  5. package/dist/ollama/index.js +13 -44
  6. package/dist/ollama/index.js.map +1 -1
  7. package/dist/openai/index.d.ts +46 -27
  8. package/dist/openai/index.js +2 -116
  9. package/dist/openai/index.js.map +1 -1
  10. package/dist/openrouter/index.d.ts +23 -10
  11. package/dist/openrouter/index.js +2 -85
  12. package/dist/openrouter/index.js.map +1 -1
  13. package/dist/xai/index.d.ts +59 -35
  14. package/dist/xai/index.js +3 -119
  15. package/dist/xai/index.js.map +1 -1
  16. package/package.json +1 -1
  17. package/src/openai/index.ts +2 -1
  18. package/src/openrouter/index.ts +2 -1
  19. package/src/providers/anthropic/transform.ts +7 -29
  20. package/src/providers/google/transform.ts +9 -49
  21. package/src/providers/ollama/transform.ts +27 -49
  22. package/src/providers/openai/index.ts +12 -8
  23. package/src/providers/openai/llm.completions.ts +9 -9
  24. package/src/providers/openai/llm.responses.ts +9 -9
  25. package/src/providers/openai/transform.completions.ts +12 -79
  26. package/src/providers/openai/transform.responses.ts +12 -54
  27. package/src/providers/openai/types.ts +54 -31
  28. package/src/providers/openrouter/index.ts +12 -8
  29. package/src/providers/openrouter/llm.completions.ts +9 -9
  30. package/src/providers/openrouter/llm.responses.ts +9 -9
  31. package/src/providers/openrouter/transform.completions.ts +12 -79
  32. package/src/providers/openrouter/transform.responses.ts +12 -25
  33. package/src/providers/openrouter/types.ts +22 -28
  34. package/src/providers/xai/index.ts +15 -10
  35. package/src/providers/xai/llm.completions.ts +9 -9
  36. package/src/providers/xai/llm.messages.ts +9 -9
  37. package/src/providers/xai/llm.responses.ts +9 -9
  38. package/src/providers/xai/transform.completions.ts +12 -64
  39. package/src/providers/xai/transform.messages.ts +11 -30
  40. package/src/providers/xai/transform.responses.ts +12 -51
  41. package/src/providers/xai/types.ts +68 -38
  42. package/src/xai/index.ts +3 -1
@@ -25,6 +25,10 @@ import type {
25
25
 
26
26
  /**
27
27
  * Transform UPP request to Anthropic format
28
+ *
29
+ * Params are spread directly to allow pass-through of any Anthropic API fields,
30
+ * even those not explicitly defined in our type. This enables developers to
31
+ * use new API features without waiting for library updates.
28
32
  */
29
33
  export function transformRequest<TParams extends AnthropicLLMParams>(
30
34
  request: LLMRequest<TParams>,
@@ -32,45 +36,19 @@ export function transformRequest<TParams extends AnthropicLLMParams>(
32
36
  ): AnthropicRequest {
33
37
  const params = (request.params ?? {}) as AnthropicLLMParams;
34
38
 
39
+ // Spread params to pass through all fields, then set required fields
35
40
  const anthropicRequest: AnthropicRequest = {
41
+ ...params,
36
42
  model: modelId,
37
43
  messages: request.messages.map(transformMessage),
38
44
  };
39
45
 
40
- // Only include max_tokens if provided - let Anthropic API enforce its requirement
41
- if (params.max_tokens !== undefined) {
42
- anthropicRequest.max_tokens = params.max_tokens;
43
- }
44
-
45
46
  // System prompt (top-level in Anthropic)
46
47
  if (request.system) {
47
48
  anthropicRequest.system = request.system;
48
49
  }
49
50
 
50
- // Model parameters
51
- if (params.temperature !== undefined) {
52
- anthropicRequest.temperature = params.temperature;
53
- }
54
- if (params.top_p !== undefined) {
55
- anthropicRequest.top_p = params.top_p;
56
- }
57
- if (params.top_k !== undefined) {
58
- anthropicRequest.top_k = params.top_k;
59
- }
60
- if (params.stop_sequences) {
61
- anthropicRequest.stop_sequences = params.stop_sequences;
62
- }
63
- if (params.metadata) {
64
- anthropicRequest.metadata = params.metadata;
65
- }
66
- if (params.thinking) {
67
- anthropicRequest.thinking = params.thinking;
68
- }
69
- if (params.service_tier !== undefined) {
70
- anthropicRequest.service_tier = params.service_tier;
71
- }
72
-
73
- // Tools
51
+ // Tools come from request, not params
74
52
  if (request.tools && request.tools.length > 0) {
75
53
  anthropicRequest.tools = request.tools.map(transformTool);
76
54
  anthropicRequest.tool_choice = { type: 'auto' };
@@ -23,6 +23,10 @@ import type {
23
23
 
24
24
  /**
25
25
  * Transform UPP request to Google format
26
+ *
27
+ * Params are spread into generationConfig to allow pass-through of any Google API fields,
28
+ * even those not explicitly defined in our type. This enables developers to
29
+ * use new API features without waiting for library updates.
26
30
  */
27
31
  export function transformRequest<TParams extends GoogleLLMParams>(
28
32
  request: LLMRequest<TParams>,
@@ -41,54 +45,10 @@ export function transformRequest<TParams extends GoogleLLMParams>(
41
45
  };
42
46
  }
43
47
 
44
- // Generation config
45
- const generationConfig: NonNullable<GoogleRequest['generationConfig']> = {};
46
-
47
- if (params.maxOutputTokens !== undefined) {
48
- generationConfig.maxOutputTokens = params.maxOutputTokens;
49
- }
50
- if (params.temperature !== undefined) {
51
- generationConfig.temperature = params.temperature;
52
- }
53
- if (params.topP !== undefined) {
54
- generationConfig.topP = params.topP;
55
- }
56
- if (params.topK !== undefined) {
57
- generationConfig.topK = params.topK;
58
- }
59
- if (params.stopSequences !== undefined) {
60
- generationConfig.stopSequences = params.stopSequences;
61
- }
62
- if (params.candidateCount !== undefined) {
63
- generationConfig.candidateCount = params.candidateCount;
64
- }
65
- if (params.responseMimeType !== undefined) {
66
- generationConfig.responseMimeType = params.responseMimeType;
67
- }
68
- if (params.responseSchema !== undefined) {
69
- generationConfig.responseSchema = params.responseSchema as Record<string, unknown>;
70
- }
71
- if (params.presencePenalty !== undefined) {
72
- generationConfig.presencePenalty = params.presencePenalty;
73
- }
74
- if (params.frequencyPenalty !== undefined) {
75
- generationConfig.frequencyPenalty = params.frequencyPenalty;
76
- }
77
- if (params.seed !== undefined) {
78
- generationConfig.seed = params.seed;
79
- }
80
- if (params.responseLogprobs !== undefined) {
81
- generationConfig.responseLogprobs = params.responseLogprobs;
82
- }
83
- if (params.logprobs !== undefined) {
84
- generationConfig.logprobs = params.logprobs;
85
- }
86
- if (params.audioTimestamp !== undefined) {
87
- generationConfig.audioTimestamp = params.audioTimestamp;
88
- }
89
- if (params.thinkingConfig !== undefined) {
90
- generationConfig.thinkingConfig = params.thinkingConfig;
91
- }
48
+ // Spread params into generationConfig to pass through all fields
49
+ const generationConfig: NonNullable<GoogleRequest['generationConfig']> = {
50
+ ...params,
51
+ };
92
52
 
93
53
  // Protocol-level structured output (overrides provider-specific settings)
94
54
  if (request.structure) {
@@ -100,7 +60,7 @@ export function transformRequest<TParams extends GoogleLLMParams>(
100
60
  googleRequest.generationConfig = generationConfig;
101
61
  }
102
62
 
103
- // Tools
63
+ // Tools come from request, not params
104
64
  if (request.tools && request.tools.length > 0) {
105
65
  googleRequest.tools = [
106
66
  {
@@ -23,6 +23,14 @@ import type {
23
23
 
24
24
  /**
25
25
  * Transform UPP request to Ollama format
26
+ *
27
+ * Params are spread to allow pass-through of any Ollama API fields,
28
+ * even those not explicitly defined in our type. This enables developers to
29
+ * use new API features without waiting for library updates.
30
+ *
31
+ * Note: Ollama uses nested 'options' for model parameters. Params that belong
32
+ * in options (like temperature, top_p, etc.) are spread into options, while
33
+ * top-level params (like keep_alive, think) are spread at the request level.
26
34
  */
27
35
  export function transformRequest<TParams extends OllamaLLMParams>(
28
36
  request: LLMRequest<TParams>,
@@ -30,63 +38,33 @@ export function transformRequest<TParams extends OllamaLLMParams>(
30
38
  ): OllamaRequest {
31
39
  const params = (request.params ?? {}) as OllamaLLMParams;
32
40
 
41
+ // Extract top-level params vs options params
42
+ const {
43
+ keep_alive,
44
+ think,
45
+ logprobs,
46
+ top_logprobs,
47
+ ...optionsParams
48
+ } = params;
49
+
50
+ // Spread params to pass through all fields, then set required fields
33
51
  const ollamaRequest: OllamaRequest = {
34
52
  model: modelId,
35
53
  messages: transformMessages(request.messages, request.system),
36
54
  };
37
55
 
38
- // Build options object for runtime parameters
39
- const options: OllamaOptions = {};
40
-
41
- if (params.num_predict !== undefined) options.num_predict = params.num_predict;
42
- if (params.temperature !== undefined) options.temperature = params.temperature;
43
- if (params.top_p !== undefined) options.top_p = params.top_p;
44
- if (params.top_k !== undefined) options.top_k = params.top_k;
45
- if (params.min_p !== undefined) options.min_p = params.min_p;
46
- if (params.typical_p !== undefined) options.typical_p = params.typical_p;
47
- if (params.repeat_penalty !== undefined) options.repeat_penalty = params.repeat_penalty;
48
- if (params.repeat_last_n !== undefined) options.repeat_last_n = params.repeat_last_n;
49
- if (params.presence_penalty !== undefined) options.presence_penalty = params.presence_penalty;
50
- if (params.frequency_penalty !== undefined) options.frequency_penalty = params.frequency_penalty;
51
- if (params.mirostat !== undefined) options.mirostat = params.mirostat;
52
- if (params.mirostat_eta !== undefined) options.mirostat_eta = params.mirostat_eta;
53
- if (params.mirostat_tau !== undefined) options.mirostat_tau = params.mirostat_tau;
54
- if (params.penalize_newline !== undefined) options.penalize_newline = params.penalize_newline;
55
- if (params.stop !== undefined) options.stop = params.stop;
56
- if (params.seed !== undefined) options.seed = params.seed;
57
- if (params.num_keep !== undefined) options.num_keep = params.num_keep;
58
- if (params.num_ctx !== undefined) options.num_ctx = params.num_ctx;
59
- if (params.num_batch !== undefined) options.num_batch = params.num_batch;
60
- if (params.num_thread !== undefined) options.num_thread = params.num_thread;
61
- if (params.num_gpu !== undefined) options.num_gpu = params.num_gpu;
62
- if (params.main_gpu !== undefined) options.main_gpu = params.main_gpu;
63
- if (params.low_vram !== undefined) options.low_vram = params.low_vram;
64
- if (params.f16_kv !== undefined) options.f16_kv = params.f16_kv;
65
- if (params.use_mmap !== undefined) options.use_mmap = params.use_mmap;
66
- if (params.use_mlock !== undefined) options.use_mlock = params.use_mlock;
67
- if (params.vocab_only !== undefined) options.vocab_only = params.vocab_only;
68
- if (params.numa !== undefined) options.numa = params.numa;
69
- if (params.tfs_z !== undefined) options.tfs_z = params.tfs_z;
70
-
71
- if (Object.keys(options).length > 0) {
72
- ollamaRequest.options = options;
73
- }
56
+ // Add top-level params if provided
57
+ if (keep_alive !== undefined) ollamaRequest.keep_alive = keep_alive;
58
+ if (think !== undefined) ollamaRequest.think = think;
59
+ if (logprobs !== undefined) ollamaRequest.logprobs = logprobs;
60
+ if (top_logprobs !== undefined) ollamaRequest.top_logprobs = top_logprobs;
74
61
 
75
- // Top-level parameters
76
- if (params.keep_alive !== undefined) {
77
- ollamaRequest.keep_alive = params.keep_alive;
78
- }
79
- if (params.think !== undefined) {
80
- ollamaRequest.think = params.think;
81
- }
82
- if (params.logprobs !== undefined) {
83
- ollamaRequest.logprobs = params.logprobs;
84
- }
85
- if (params.top_logprobs !== undefined) {
86
- ollamaRequest.top_logprobs = params.top_logprobs;
62
+ // Spread remaining params into options to pass through all model parameters
63
+ if (Object.keys(optionsParams).length > 0) {
64
+ ollamaRequest.options = optionsParams as OllamaOptions;
87
65
  }
88
66
 
89
- // Tools
67
+ // Tools come from request, not params
90
68
  if (request.tools && request.tools.length > 0) {
91
69
  ollamaRequest.tools = request.tools.map(transformTool);
92
70
  }
@@ -6,7 +6,10 @@ import type {
6
6
  } from '../../types/provider.ts';
7
7
  import { createCompletionsLLMHandler } from './llm.completions.ts';
8
8
  import { createResponsesLLMHandler } from './llm.responses.ts';
9
- import type { OpenAILLMParams, OpenAIConfig } from './types.ts';
9
+ import type { OpenAICompletionsParams, OpenAIResponsesParams, OpenAIConfig } from './types.ts';
10
+
11
+ /** Union type for modalities interface */
12
+ type OpenAILLMParamsUnion = OpenAICompletionsParams | OpenAIResponsesParams;
10
13
 
11
14
  /**
12
15
  * OpenAI provider options
@@ -51,7 +54,7 @@ export interface OpenAIProvider extends Provider<OpenAIProviderOptions> {
51
54
 
52
55
  /** Supported modalities */
53
56
  readonly modalities: {
54
- llm: LLMHandler<OpenAILLMParams>;
57
+ llm: LLMHandler<OpenAILLMParamsUnion>;
55
58
  };
56
59
  }
57
60
 
@@ -77,10 +80,10 @@ function createOpenAIProvider(): OpenAIProvider {
77
80
 
78
81
  // Create a dynamic modalities object that returns the correct handler
79
82
  const modalities = {
80
- get llm(): LLMHandler<OpenAILLMParams> {
83
+ get llm(): LLMHandler<OpenAILLMParamsUnion> {
81
84
  return currentApiMode === 'completions'
82
- ? completionsHandler
83
- : responsesHandler;
85
+ ? (completionsHandler as unknown as LLMHandler<OpenAILLMParamsUnion>)
86
+ : (responsesHandler as unknown as LLMHandler<OpenAILLMParamsUnion>);
84
87
  },
85
88
  };
86
89
 
@@ -106,8 +109,8 @@ function createOpenAIProvider(): OpenAIProvider {
106
109
  const provider = fn as OpenAIProvider;
107
110
 
108
111
  // Inject provider reference into both handlers (spec compliance)
109
- responsesHandler._setProvider?.(provider as unknown as LLMProvider<OpenAILLMParams>);
110
- completionsHandler._setProvider?.(provider as unknown as LLMProvider<OpenAILLMParams>);
112
+ responsesHandler._setProvider?.(provider as unknown as LLMProvider<OpenAIResponsesParams>);
113
+ completionsHandler._setProvider?.(provider as unknown as LLMProvider<OpenAICompletionsParams>);
111
114
 
112
115
  return provider;
113
116
  }
@@ -143,7 +146,8 @@ export const openai = createOpenAIProvider();
143
146
 
144
147
  // Re-export types
145
148
  export type {
146
- OpenAILLMParams,
149
+ OpenAICompletionsParams,
150
+ OpenAIResponsesParams,
147
151
  OpenAIConfig,
148
152
  OpenAIAPIMode,
149
153
  OpenAIModelOptions,
@@ -6,7 +6,7 @@ import { resolveApiKey } from '../../http/keys.ts';
6
6
  import { doFetch, doStreamFetch } from '../../http/fetch.ts';
7
7
  import { parseSSEStream } from '../../http/sse.ts';
8
8
  import { normalizeHttpError } from '../../http/errors.ts';
9
- import type { OpenAILLMParams, OpenAICompletionsResponse, OpenAICompletionsStreamChunk } from './types.ts';
9
+ import type { OpenAICompletionsParams, OpenAICompletionsResponse, OpenAICompletionsStreamChunk } from './types.ts';
10
10
  import {
11
11
  transformRequest,
12
12
  transformResponse,
@@ -32,16 +32,16 @@ const OPENAI_CAPABILITIES: LLMCapabilities = {
32
32
  /**
33
33
  * Create OpenAI Chat Completions LLM handler
34
34
  */
35
- export function createCompletionsLLMHandler(): LLMHandler<OpenAILLMParams> {
35
+ export function createCompletionsLLMHandler(): LLMHandler<OpenAICompletionsParams> {
36
36
  // Provider reference injected by createProvider() or OpenAI's custom factory
37
- let providerRef: LLMProvider<OpenAILLMParams> | null = null;
37
+ let providerRef: LLMProvider<OpenAICompletionsParams> | null = null;
38
38
 
39
39
  return {
40
- _setProvider(provider: LLMProvider<OpenAILLMParams>) {
40
+ _setProvider(provider: LLMProvider<OpenAICompletionsParams>) {
41
41
  providerRef = provider;
42
42
  },
43
43
 
44
- bind(modelId: string): BoundLLMModel<OpenAILLMParams> {
44
+ bind(modelId: string): BoundLLMModel<OpenAICompletionsParams> {
45
45
  // Use the injected provider reference
46
46
  if (!providerRef) {
47
47
  throw new UPPError(
@@ -52,15 +52,15 @@ export function createCompletionsLLMHandler(): LLMHandler<OpenAILLMParams> {
52
52
  );
53
53
  }
54
54
 
55
- const model: BoundLLMModel<OpenAILLMParams> = {
55
+ const model: BoundLLMModel<OpenAICompletionsParams> = {
56
56
  modelId,
57
57
  capabilities: OPENAI_CAPABILITIES,
58
58
 
59
- get provider(): LLMProvider<OpenAILLMParams> {
59
+ get provider(): LLMProvider<OpenAICompletionsParams> {
60
60
  return providerRef!;
61
61
  },
62
62
 
63
- async complete(request: LLMRequest<OpenAILLMParams>): Promise<LLMResponse> {
63
+ async complete(request: LLMRequest<OpenAICompletionsParams>): Promise<LLMResponse> {
64
64
  const apiKey = await resolveApiKey(
65
65
  request.config,
66
66
  'OPENAI_API_KEY',
@@ -91,7 +91,7 @@ export function createCompletionsLLMHandler(): LLMHandler<OpenAILLMParams> {
91
91
  return transformResponse(data);
92
92
  },
93
93
 
94
- stream(request: LLMRequest<OpenAILLMParams>): LLMStreamResult {
94
+ stream(request: LLMRequest<OpenAICompletionsParams>): LLMStreamResult {
95
95
  const state = createStreamState();
96
96
  let responseResolve: (value: LLMResponse) => void;
97
97
  let responseReject: (error: Error) => void;
@@ -6,7 +6,7 @@ import { resolveApiKey } from '../../http/keys.ts';
6
6
  import { doFetch, doStreamFetch } from '../../http/fetch.ts';
7
7
  import { parseSSEStream } from '../../http/sse.ts';
8
8
  import { normalizeHttpError } from '../../http/errors.ts';
9
- import type { OpenAILLMParams, OpenAIResponsesResponse, OpenAIResponsesStreamEvent, OpenAIResponseErrorEvent } from './types.ts';
9
+ import type { OpenAIResponsesParams, OpenAIResponsesResponse, OpenAIResponsesStreamEvent, OpenAIResponseErrorEvent } from './types.ts';
10
10
  import {
11
11
  transformRequest,
12
12
  transformResponse,
@@ -32,16 +32,16 @@ const OPENAI_CAPABILITIES: LLMCapabilities = {
32
32
  /**
33
33
  * Create OpenAI Responses API LLM handler
34
34
  */
35
- export function createResponsesLLMHandler(): LLMHandler<OpenAILLMParams> {
35
+ export function createResponsesLLMHandler(): LLMHandler<OpenAIResponsesParams> {
36
36
  // Provider reference injected by createProvider() or OpenAI's custom factory
37
- let providerRef: LLMProvider<OpenAILLMParams> | null = null;
37
+ let providerRef: LLMProvider<OpenAIResponsesParams> | null = null;
38
38
 
39
39
  return {
40
- _setProvider(provider: LLMProvider<OpenAILLMParams>) {
40
+ _setProvider(provider: LLMProvider<OpenAIResponsesParams>) {
41
41
  providerRef = provider;
42
42
  },
43
43
 
44
- bind(modelId: string): BoundLLMModel<OpenAILLMParams> {
44
+ bind(modelId: string): BoundLLMModel<OpenAIResponsesParams> {
45
45
  // Use the injected provider reference
46
46
  if (!providerRef) {
47
47
  throw new UPPError(
@@ -52,15 +52,15 @@ export function createResponsesLLMHandler(): LLMHandler<OpenAILLMParams> {
52
52
  );
53
53
  }
54
54
 
55
- const model: BoundLLMModel<OpenAILLMParams> = {
55
+ const model: BoundLLMModel<OpenAIResponsesParams> = {
56
56
  modelId,
57
57
  capabilities: OPENAI_CAPABILITIES,
58
58
 
59
- get provider(): LLMProvider<OpenAILLMParams> {
59
+ get provider(): LLMProvider<OpenAIResponsesParams> {
60
60
  return providerRef!;
61
61
  },
62
62
 
63
- async complete(request: LLMRequest<OpenAILLMParams>): Promise<LLMResponse> {
63
+ async complete(request: LLMRequest<OpenAIResponsesParams>): Promise<LLMResponse> {
64
64
  const apiKey = await resolveApiKey(
65
65
  request.config,
66
66
  'OPENAI_API_KEY',
@@ -102,7 +102,7 @@ export function createResponsesLLMHandler(): LLMHandler<OpenAILLMParams> {
102
102
  return transformResponse(data);
103
103
  },
104
104
 
105
- stream(request: LLMRequest<OpenAILLMParams>): LLMStreamResult {
105
+ stream(request: LLMRequest<OpenAIResponsesParams>): LLMStreamResult {
106
106
  const state = createStreamState();
107
107
  let responseResolve: (value: LLMResponse) => void;
108
108
  let responseReject: (error: Error) => void;
@@ -11,7 +11,7 @@ import {
11
11
  isToolResultMessage,
12
12
  } from '../../types/messages.ts';
13
13
  import type {
14
- OpenAILLMParams,
14
+ OpenAICompletionsParams,
15
15
  OpenAICompletionsRequest,
16
16
  OpenAICompletionsMessage,
17
17
  OpenAIUserContent,
@@ -23,94 +23,30 @@ import type {
23
23
 
24
24
  /**
25
25
  * Transform UPP request to OpenAI Chat Completions format
26
+ *
27
+ * Params are spread directly to allow pass-through of any OpenAI API fields,
28
+ * even those not explicitly defined in our type. This enables developers to
29
+ * use new API features without waiting for library updates.
26
30
  */
27
- export function transformRequest<TParams extends OpenAILLMParams>(
28
- request: LLMRequest<TParams>,
31
+ export function transformRequest(
32
+ request: LLMRequest<OpenAICompletionsParams>,
29
33
  modelId: string
30
34
  ): OpenAICompletionsRequest {
31
- const params: OpenAILLMParams = request.params ?? {};
35
+ const params = request.params ?? ({} as OpenAICompletionsParams);
32
36
 
37
+ // Spread params to pass through all fields, then set required fields
33
38
  const openaiRequest: OpenAICompletionsRequest = {
39
+ ...params,
34
40
  model: modelId,
35
41
  messages: transformMessages(request.messages, request.system),
36
42
  };
37
43
 
38
- // Model parameters
39
- if (params.temperature !== undefined) {
40
- openaiRequest.temperature = params.temperature;
41
- }
42
- if (params.top_p !== undefined) {
43
- openaiRequest.top_p = params.top_p;
44
- }
45
- if (params.max_completion_tokens !== undefined) {
46
- openaiRequest.max_completion_tokens = params.max_completion_tokens;
47
- } else if (params.max_tokens !== undefined) {
48
- openaiRequest.max_tokens = params.max_tokens;
49
- }
50
- if (params.frequency_penalty !== undefined) {
51
- openaiRequest.frequency_penalty = params.frequency_penalty;
52
- }
53
- if (params.presence_penalty !== undefined) {
54
- openaiRequest.presence_penalty = params.presence_penalty;
55
- }
56
- if (params.stop !== undefined) {
57
- openaiRequest.stop = params.stop;
58
- }
59
- if (params.n !== undefined) {
60
- openaiRequest.n = params.n;
61
- }
62
- if (params.logprobs !== undefined) {
63
- openaiRequest.logprobs = params.logprobs;
64
- }
65
- if (params.top_logprobs !== undefined) {
66
- openaiRequest.top_logprobs = params.top_logprobs;
67
- }
68
- if (params.seed !== undefined) {
69
- openaiRequest.seed = params.seed;
70
- }
71
- if (params.user !== undefined) {
72
- openaiRequest.user = params.user;
73
- }
74
- if (params.logit_bias !== undefined) {
75
- openaiRequest.logit_bias = params.logit_bias;
76
- }
77
- if (params.reasoning_effort !== undefined) {
78
- openaiRequest.reasoning_effort = params.reasoning_effort;
79
- }
80
- if (params.verbosity !== undefined) {
81
- openaiRequest.verbosity = params.verbosity;
82
- }
83
- if (params.service_tier !== undefined) {
84
- openaiRequest.service_tier = params.service_tier;
85
- }
86
- if (params.store !== undefined) {
87
- openaiRequest.store = params.store;
88
- }
89
- if (params.metadata !== undefined) {
90
- openaiRequest.metadata = params.metadata;
91
- }
92
- if (params.prediction !== undefined) {
93
- openaiRequest.prediction = params.prediction;
94
- }
95
- if (params.prompt_cache_key !== undefined) {
96
- openaiRequest.prompt_cache_key = params.prompt_cache_key;
97
- }
98
- if (params.prompt_cache_retention !== undefined) {
99
- openaiRequest.prompt_cache_retention = params.prompt_cache_retention;
100
- }
101
- if (params.safety_identifier !== undefined) {
102
- openaiRequest.safety_identifier = params.safety_identifier;
103
- }
104
-
105
- // Tools
44
+ // Tools come from request, not params
106
45
  if (request.tools && request.tools.length > 0) {
107
46
  openaiRequest.tools = request.tools.map(transformTool);
108
- if (params.parallel_tool_calls !== undefined) {
109
- openaiRequest.parallel_tool_calls = params.parallel_tool_calls;
110
- }
111
47
  }
112
48
 
113
- // Structured output via response_format
49
+ // Structured output via response_format (overrides params.response_format if set)
114
50
  if (request.structure) {
115
51
  const schema: Record<string, unknown> = {
116
52
  type: 'object',
@@ -133,9 +69,6 @@ export function transformRequest<TParams extends OpenAILLMParams>(
133
69
  strict: true,
134
70
  },
135
71
  };
136
- } else if (params.response_format !== undefined) {
137
- // Pass through response_format from params if no structure is defined
138
- openaiRequest.response_format = params.response_format;
139
72
  }
140
73
 
141
74
  return openaiRequest;
@@ -11,7 +11,7 @@ import {
11
11
  isToolResultMessage,
12
12
  } from '../../types/messages.ts';
13
13
  import type {
14
- OpenAILLMParams,
14
+ OpenAIResponsesParams,
15
15
  OpenAIResponsesRequest,
16
16
  OpenAIResponsesInputItem,
17
17
  OpenAIResponsesContentPart,
@@ -25,72 +25,30 @@ import type {
25
25
 
26
26
  /**
27
27
  * Transform UPP request to OpenAI Responses API format
28
+ *
29
+ * Params are spread directly to allow pass-through of any OpenAI API fields,
30
+ * even those not explicitly defined in our type. This enables developers to
31
+ * use new API features without waiting for library updates.
28
32
  */
29
- export function transformRequest<TParams extends OpenAILLMParams>(
30
- request: LLMRequest<TParams>,
33
+ export function transformRequest(
34
+ request: LLMRequest<OpenAIResponsesParams>,
31
35
  modelId: string
32
36
  ): OpenAIResponsesRequest {
33
- const params: OpenAILLMParams = request.params ?? {};
37
+ const params = request.params ?? ({} as OpenAIResponsesParams);
34
38
 
39
+ // Spread params to pass through all fields, then set required fields
35
40
  const openaiRequest: OpenAIResponsesRequest = {
41
+ ...params,
36
42
  model: modelId,
37
43
  input: transformInputItems(request.messages, request.system),
38
44
  };
39
45
 
40
- // Model parameters
41
- if (params.temperature !== undefined) {
42
- openaiRequest.temperature = params.temperature;
43
- }
44
- if (params.top_p !== undefined) {
45
- openaiRequest.top_p = params.top_p;
46
- }
47
- if (params.max_output_tokens !== undefined) {
48
- openaiRequest.max_output_tokens = params.max_output_tokens;
49
- } else if (params.max_completion_tokens !== undefined) {
50
- openaiRequest.max_output_tokens = params.max_completion_tokens;
51
- } else if (params.max_tokens !== undefined) {
52
- openaiRequest.max_output_tokens = params.max_tokens;
53
- }
54
- if (params.service_tier !== undefined) {
55
- openaiRequest.service_tier = params.service_tier;
56
- }
57
- if (params.store !== undefined) {
58
- openaiRequest.store = params.store;
59
- }
60
- if (params.metadata !== undefined) {
61
- openaiRequest.metadata = params.metadata;
62
- }
63
- if (params.truncation !== undefined) {
64
- openaiRequest.truncation = params.truncation;
65
- }
66
- if (params.include !== undefined) {
67
- openaiRequest.include = params.include;
68
- }
69
- if (params.background !== undefined) {
70
- openaiRequest.background = params.background;
71
- }
72
- if (params.previous_response_id !== undefined) {
73
- openaiRequest.previous_response_id = params.previous_response_id;
74
- }
75
- if (params.reasoning !== undefined) {
76
- openaiRequest.reasoning = { ...params.reasoning };
77
- }
78
- if (params.reasoning_effort !== undefined) {
79
- openaiRequest.reasoning = {
80
- ...(openaiRequest.reasoning ?? {}),
81
- effort: params.reasoning_effort,
82
- };
83
- }
84
-
85
- // Tools
46
+ // Tools come from request, not params
86
47
  if (request.tools && request.tools.length > 0) {
87
48
  openaiRequest.tools = request.tools.map(transformTool);
88
- if (params.parallel_tool_calls !== undefined) {
89
- openaiRequest.parallel_tool_calls = params.parallel_tool_calls;
90
- }
91
49
  }
92
50
 
93
- // Structured output via text.format
51
+ // Structured output via text.format (overrides params.text if set)
94
52
  if (request.structure) {
95
53
  const schema: Record<string, unknown> = {
96
54
  type: 'object',