mirror of
https://github.com/strands-agents/harness-sdk.git
synced 2026-10-02 02:44:48 +08:00
feat: centralize model defaults; emit warnings when defaults are used… (#909)
Co-authored-by: Owen Kaplan <okapl@amazon.com>
This commit is contained in:
co-authored by
Owen Kaplan
parent
ad8006f46b
commit
c0992343e9
@@ -58,6 +58,7 @@ sdk-typescript/
|
||||
│ │ ├── logging/ # Structured logging
|
||||
│ │ │ ├── __tests__/
|
||||
│ │ │ ├── logger.ts
|
||||
│ │ │ ├── warn-once.ts # Dedupe warnings by message content
|
||||
│ │ │ ├── types.ts
|
||||
│ │ │ └── index.ts
|
||||
│ │ │
|
||||
@@ -68,6 +69,7 @@ sdk-typescript/
|
||||
│ │ │ ├── bedrock.ts # AWS Bedrock
|
||||
│ │ │ ├── openai.ts # OpenAI
|
||||
│ │ │ ├── vercel.ts # Vercel AI SDK
|
||||
│ │ │ ├── defaults.ts # Centralized model defaults + warning messages
|
||||
│ │ │ ├── model.ts # Base model interface
|
||||
│ │ │ └── streaming.ts # Streaming event types
|
||||
│ │ │
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import { describe, it, expect, vi } from 'vitest'
|
||||
import type { Logger } from '../types.js'
|
||||
import { warnOnce } from '../warn-once.js'
|
||||
|
||||
function createLogger(): Logger {
|
||||
return { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() }
|
||||
}
|
||||
|
||||
describe('warnOnce', () => {
|
||||
it('emits a warning the first time a message is seen', () => {
|
||||
const logger = createLogger()
|
||||
warnOnce(logger, 'first-seen-msg')
|
||||
expect(logger.warn).toHaveBeenCalledTimes(1)
|
||||
expect(logger.warn).toHaveBeenCalledWith('first-seen-msg')
|
||||
})
|
||||
|
||||
it('does not emit repeated warnings for the same message', () => {
|
||||
const logger = createLogger()
|
||||
warnOnce(logger, 'repeated-msg')
|
||||
warnOnce(logger, 'repeated-msg')
|
||||
warnOnce(logger, 'repeated-msg')
|
||||
expect(logger.warn).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('emits distinct messages independently', () => {
|
||||
const logger = createLogger()
|
||||
warnOnce(logger, 'distinct-alpha-msg')
|
||||
warnOnce(logger, 'distinct-beta-msg')
|
||||
warnOnce(logger, 'distinct-alpha-msg')
|
||||
expect(logger.warn).toHaveBeenCalledTimes(2)
|
||||
expect(logger.warn).toHaveBeenNthCalledWith(1, 'distinct-alpha-msg')
|
||||
expect(logger.warn).toHaveBeenNthCalledWith(2, 'distinct-beta-msg')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,19 @@
|
||||
import type { Logger } from './types.js'
|
||||
|
||||
const warned = new Set<string>()
|
||||
|
||||
/**
|
||||
* Emits a warning log at most once per unique message per process.
|
||||
*
|
||||
* Subsequent calls with the same message are no-ops, which prevents
|
||||
* repeated nudges (e.g. "using default modelId") from flooding logs
|
||||
* when many instances are constructed.
|
||||
*
|
||||
* @param logger - Logger to emit the warning on
|
||||
* @param msg - Warning message; also used as the dedupe key
|
||||
*/
|
||||
export function warnOnce(logger: Logger, msg: string): void {
|
||||
if (warned.has(msg)) return
|
||||
logger.warn(msg)
|
||||
warned.add(msg)
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
JsonBlock,
|
||||
} from '../../types/messages.js'
|
||||
import { ImageBlock, DocumentBlock, VideoBlock } from '../../types/media.js'
|
||||
import { warnOnce } from '../../logging/warn-once.js'
|
||||
|
||||
/**
|
||||
* Helper to create a mock Anthropic client with streaming support
|
||||
@@ -39,10 +40,13 @@ vi.mock('@anthropic-ai/sdk', () => {
|
||||
}
|
||||
})
|
||||
|
||||
vi.mock('../../logging/warn-once.js', () => ({
|
||||
warnOnce: vi.fn(),
|
||||
}))
|
||||
|
||||
describe('AnthropicModel', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
vi.spyOn(console, 'warn').mockImplementation(() => {})
|
||||
if (isNode) {
|
||||
vi.stubEnv('ANTHROPIC_API_KEY', 'sk-ant-test-env')
|
||||
}
|
||||
@@ -101,12 +105,34 @@ describe('AnthropicModel', () => {
|
||||
|
||||
it('warns when maxTokens is not explicitly set', () => {
|
||||
new AnthropicModel({ apiKey: 'sk-ant-test' })
|
||||
expect(console.warn).toHaveBeenCalledWith(expect.stringContaining('using default maxTokens'))
|
||||
expect(warnOnce).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ warn: expect.any(Function) }),
|
||||
expect.stringContaining('using default maxTokens')
|
||||
)
|
||||
})
|
||||
|
||||
it('does not warn when maxTokens is explicitly set', () => {
|
||||
new AnthropicModel({ apiKey: 'sk-ant-test', maxTokens: 4096 })
|
||||
expect(console.warn).not.toHaveBeenCalledWith(expect.stringContaining('using default maxTokens'))
|
||||
expect(warnOnce).not.toHaveBeenCalledWith(
|
||||
expect.objectContaining({ warn: expect.any(Function) }),
|
||||
expect.stringContaining('using default maxTokens')
|
||||
)
|
||||
})
|
||||
|
||||
it('warns when modelId is not explicitly set', () => {
|
||||
new AnthropicModel({ apiKey: 'sk-ant-test' })
|
||||
expect(warnOnce).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ warn: expect.any(Function) }),
|
||||
expect.stringContaining('using default modelId')
|
||||
)
|
||||
})
|
||||
|
||||
it('does not warn when modelId is explicitly set', () => {
|
||||
new AnthropicModel({ apiKey: 'sk-ant-test', modelId: 'claude-3-opus-20240229' })
|
||||
expect(warnOnce).not.toHaveBeenCalledWith(
|
||||
expect.objectContaining({ warn: expect.any(Function) }),
|
||||
expect.stringContaining('using default modelId')
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import { CitationsBlock } from '../../types/citations.js'
|
||||
import type { StreamOptions } from '../model.js'
|
||||
import { collectIterator } from '../../__fixtures__/model-test-helpers.js'
|
||||
import { NOOP_TOOL_SPEC } from '../../tools/noop-tool.js'
|
||||
import { warnOnce } from '../../logging/warn-once.js'
|
||||
|
||||
/**
|
||||
* Helper function to mock BedrockRuntimeClient implementation with customizable config.
|
||||
@@ -140,6 +141,10 @@ vi.mock('@aws-sdk/client-bedrock-runtime', async (importOriginal) => {
|
||||
}
|
||||
})
|
||||
|
||||
vi.mock('../../logging/warn-once.js', () => ({
|
||||
warnOnce: vi.fn(),
|
||||
}))
|
||||
|
||||
describe('BedrockModel', () => {
|
||||
const BEDROCK_NOOP_TOOL_CONFIG = {
|
||||
tools: [{ toolSpec: { ...NOOP_TOOL_SPEC, inputSchema: { json: NOOP_TOOL_SPEC.inputSchema } } }],
|
||||
@@ -173,6 +178,22 @@ describe('BedrockModel', () => {
|
||||
expect(config.modelId).toBeDefined()
|
||||
})
|
||||
|
||||
it('warns when modelId is not explicitly set', () => {
|
||||
new BedrockModel()
|
||||
expect(warnOnce).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ warn: expect.any(Function) }),
|
||||
expect.stringContaining('using default modelId')
|
||||
)
|
||||
})
|
||||
|
||||
it('does not warn when modelId is explicitly set', () => {
|
||||
new BedrockModel({ modelId: 'us.anthropic.claude-3-5-sonnet-20241022-v2:0' })
|
||||
expect(warnOnce).not.toHaveBeenCalledWith(
|
||||
expect.objectContaining({ warn: expect.any(Function) }),
|
||||
expect.stringContaining('using default modelId')
|
||||
)
|
||||
})
|
||||
|
||||
it('uses provided model ID ', () => {
|
||||
const customModelId = 'us.anthropic.claude-3-5-sonnet-20241022-v2:0'
|
||||
const provider = new BedrockModel({ modelId: customModelId })
|
||||
@@ -1327,8 +1348,8 @@ describe('BedrockModel', () => {
|
||||
})
|
||||
|
||||
it('does not warn when array system prompt is provided without cacheConfig', async () => {
|
||||
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
||||
const provider = new BedrockModel()
|
||||
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
||||
const messages = [new Message({ role: 'user', content: [new TextBlock('Hello')] })]
|
||||
const options: StreamOptions = {
|
||||
systemPrompt: [
|
||||
|
||||
@@ -16,6 +16,11 @@ import type { ContentBlock } from '../../types/messages.js'
|
||||
import { formatMessages, mapChunkToEvents } from '../google/adapters.js'
|
||||
import type { GoogleStreamState } from '../google/types.js'
|
||||
import { ImageBlock, DocumentBlock, VideoBlock } from '../../types/media.js'
|
||||
import { warnOnce } from '../../logging/warn-once.js'
|
||||
|
||||
vi.mock('../../logging/warn-once.js', () => ({
|
||||
warnOnce: vi.fn(),
|
||||
}))
|
||||
|
||||
/**
|
||||
* Helper to create a mock Gemini client with streaming support
|
||||
@@ -84,6 +89,7 @@ function formatBlock(block: ContentBlock, role: 'user' | 'assistant' = 'user'):
|
||||
|
||||
describe('GoogleModel', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
vi.stubEnv('GEMINI_API_KEY', 'test-api-key')
|
||||
})
|
||||
|
||||
@@ -108,6 +114,22 @@ describe('GoogleModel', () => {
|
||||
|
||||
expect(() => new GoogleModel({ client: mockClient })).not.toThrow()
|
||||
})
|
||||
|
||||
it('warns when modelId is not explicitly set', () => {
|
||||
new GoogleModel({ apiKey: 'test-key' })
|
||||
expect(warnOnce).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ warn: expect.any(Function) }),
|
||||
expect.stringContaining('using default modelId')
|
||||
)
|
||||
})
|
||||
|
||||
it('does not warn when modelId is explicitly set', () => {
|
||||
new GoogleModel({ apiKey: 'test-key', modelId: 'gemini-2.5-flash' })
|
||||
expect(warnOnce).not.toHaveBeenCalledWith(
|
||||
expect.objectContaining({ warn: expect.any(Function) }),
|
||||
expect.stringContaining('using default modelId')
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('updateConfig', () => {
|
||||
|
||||
@@ -7,6 +7,7 @@ import { collectIterator } from '../../__fixtures__/model-test-helpers.js'
|
||||
import { Message, TextBlock, ToolUseBlock, ToolResultBlock, GuardContentBlock } from '../../types/messages.js'
|
||||
import type { SystemContentBlock } from '../../types/messages.js'
|
||||
import { ImageBlock, DocumentBlock, VideoBlock } from '../../types/media.js'
|
||||
import { warnOnce } from '../../logging/warn-once.js'
|
||||
|
||||
/**
|
||||
* Helper to create a mock OpenAI client with streaming support
|
||||
@@ -31,6 +32,10 @@ vi.mock('openai', () => {
|
||||
}
|
||||
})
|
||||
|
||||
vi.mock('../../logging/warn-once.js', () => ({
|
||||
warnOnce: vi.fn(),
|
||||
}))
|
||||
|
||||
describe('OpenAIModel', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
@@ -81,6 +86,22 @@ describe('OpenAIModel', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('warns when modelId is not explicitly set', () => {
|
||||
new OpenAIModel({ api: 'chat', apiKey: 'sk-test' })
|
||||
expect(warnOnce).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ warn: expect.any(Function) }),
|
||||
expect.stringContaining('using default modelId')
|
||||
)
|
||||
})
|
||||
|
||||
it('does not warn when modelId is explicitly set', () => {
|
||||
new OpenAIModel({ api: 'chat', modelId: 'gpt-5.4', apiKey: 'sk-test' })
|
||||
expect(warnOnce).not.toHaveBeenCalledWith(
|
||||
expect.objectContaining({ warn: expect.any(Function) }),
|
||||
expect.stringContaining('using default modelId')
|
||||
)
|
||||
})
|
||||
|
||||
it('uses API key from constructor parameter', () => {
|
||||
const apiKey = 'sk-explicit'
|
||||
new OpenAIModel({ api: 'chat', modelId: 'gpt-5.4', apiKey })
|
||||
|
||||
@@ -7,9 +7,9 @@ import { ContextWindowOverflowError, ModelThrottledError, normalizeError } from
|
||||
import type { ImageBlock, DocumentBlock } from '../types/media.js'
|
||||
import { encodeBase64 } from '../types/media.js'
|
||||
import { logger } from '../logging/logger.js'
|
||||
import { warnOnce } from '../logging/warn-once.js'
|
||||
import { MODEL_DEFAULTS, defaultMaxTokensWarningMessage, defaultModelWarningMessage } from './defaults.js'
|
||||
|
||||
const DEFAULT_ANTHROPIC_MODEL_ID = 'claude-sonnet-4-6'
|
||||
const DEFAULT_ANTHROPIC_MAX_TOKENS = 64_000
|
||||
const CONTEXT_WINDOW_OVERFLOW_ERRORS = ['prompt is too long', 'max_tokens exceeded', 'input too long']
|
||||
const TEXT_FILE_FORMATS = ['txt', 'md', 'markdown', 'csv', 'json', 'xml', 'html', 'yml', 'yaml', 'js', 'ts', 'py']
|
||||
|
||||
@@ -40,15 +40,17 @@ export class AnthropicModel extends Model<AnthropicModelConfig> {
|
||||
const { apiKey, client, clientConfig, ...modelConfig } = options || {}
|
||||
|
||||
this._config = {
|
||||
modelId: DEFAULT_ANTHROPIC_MODEL_ID,
|
||||
maxTokens: DEFAULT_ANTHROPIC_MAX_TOKENS,
|
||||
modelId: MODEL_DEFAULTS.anthropic.modelId,
|
||||
maxTokens: MODEL_DEFAULTS.anthropic.maxTokens,
|
||||
...modelConfig,
|
||||
}
|
||||
|
||||
if (modelConfig.modelId === undefined) {
|
||||
warnOnce(logger, defaultModelWarningMessage(MODEL_DEFAULTS.anthropic.modelId))
|
||||
}
|
||||
|
||||
if (modelConfig.maxTokens === undefined) {
|
||||
logger.warn(
|
||||
`max_tokens=<${DEFAULT_ANTHROPIC_MAX_TOKENS}> | using default maxTokens, which is subject to change | set maxTokens explicitly to pin the value`
|
||||
)
|
||||
warnOnce(logger, defaultMaxTokensWarningMessage(MODEL_DEFAULTS.anthropic.maxTokens))
|
||||
}
|
||||
|
||||
if (client) {
|
||||
@@ -226,7 +228,7 @@ export class AnthropicModel extends Model<AnthropicModelConfig> {
|
||||
|
||||
const request: Anthropic.MessageStreamParams = {
|
||||
model: this._config.modelId,
|
||||
max_tokens: this._config.maxTokens ?? DEFAULT_ANTHROPIC_MAX_TOKENS,
|
||||
max_tokens: this._config.maxTokens ?? MODEL_DEFAULTS.anthropic.maxTokens,
|
||||
messages: this._formatMessages(messages),
|
||||
stream: true,
|
||||
}
|
||||
|
||||
@@ -50,15 +50,10 @@ import type { JSONValue } from '../types/json.js'
|
||||
import { ContextWindowOverflowError, ModelThrottledError, normalizeError } from '../errors.js'
|
||||
import { ensureDefined } from '../types/validation.js'
|
||||
import { logger } from '../logging/logger.js'
|
||||
import { warnOnce } from '../logging/warn-once.js'
|
||||
import { NOOP_TOOL_SPEC } from '../tools/noop-tool.js'
|
||||
import { MODEL_DEFAULTS, defaultModelWarningMessage } from './defaults.js'
|
||||
|
||||
/**
|
||||
* Default Bedrock model ID.
|
||||
* Uses Claude Sonnet 4 with global inference profile for cross-region availability.
|
||||
*/
|
||||
const DEFAULT_BEDROCK_MODEL_ID = 'global.anthropic.claude-sonnet-4-6'
|
||||
|
||||
const DEFAULT_BEDROCK_REGION = 'us-west-2'
|
||||
const DEFAULT_BEDROCK_REGION_SUPPORTS_FIP = false
|
||||
|
||||
/**
|
||||
@@ -360,10 +355,14 @@ export class BedrockModel extends Model<BedrockModelConfig> {
|
||||
|
||||
// Initialize model config with default model ID if not provided
|
||||
this._config = {
|
||||
modelId: DEFAULT_BEDROCK_MODEL_ID,
|
||||
modelId: MODEL_DEFAULTS.bedrock.modelId,
|
||||
...modelConfig,
|
||||
}
|
||||
|
||||
if (modelConfig.modelId === undefined) {
|
||||
warnOnce(logger, defaultModelWarningMessage(MODEL_DEFAULTS.bedrock.modelId))
|
||||
}
|
||||
|
||||
// Build user agent string (extend if provided, otherwise use SDK identifier)
|
||||
const customUserAgent = clientConfig?.customUserAgent
|
||||
? `${clientConfig.customUserAgent} strands-agents-ts-sdk`
|
||||
@@ -1635,7 +1634,7 @@ function applyDefaultRegion(config: BedrockRuntimeClientResolvedConfig): void {
|
||||
// Note: it was observed that the browser version of the BedrockClient
|
||||
// uses a string instead of an error object - thus the normalizeError call
|
||||
if (normalizeError(error).message === 'Region is missing') {
|
||||
return DEFAULT_BEDROCK_REGION
|
||||
return MODEL_DEFAULTS.bedrock.region
|
||||
}
|
||||
|
||||
throw error
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Default values for model providers.
|
||||
*
|
||||
* These defaults are subject to change between versions. Set values explicitly
|
||||
* on model configurations to pin behavior across upgrades.
|
||||
*/
|
||||
|
||||
export const MODEL_DEFAULTS = {
|
||||
anthropic: {
|
||||
modelId: 'claude-sonnet-4-6',
|
||||
maxTokens: 64_000,
|
||||
},
|
||||
bedrock: {
|
||||
modelId: 'global.anthropic.claude-sonnet-4-6',
|
||||
region: 'us-west-2',
|
||||
},
|
||||
openai: {
|
||||
modelId: 'gpt-5.4',
|
||||
},
|
||||
gemini: {
|
||||
modelId: 'gemini-2.5-flash',
|
||||
},
|
||||
} as const
|
||||
|
||||
/**
|
||||
* Builds a warning message for when the default model ID is used.
|
||||
*
|
||||
* @param defaultModelId - The default model ID being used
|
||||
* @returns Formatted warning message string
|
||||
*/
|
||||
export function defaultModelWarningMessage(defaultModelId: string): string {
|
||||
return `model_id=<${defaultModelId}> | using default modelId, which is subject to change | set modelId explicitly to pin the value`
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a warning message for when the default max tokens value is used.
|
||||
*
|
||||
* @param defaultMaxTokens - The default max tokens value being used
|
||||
* @returns Formatted warning message string
|
||||
*/
|
||||
export function defaultMaxTokensWarningMessage(defaultMaxTokens: number): string {
|
||||
return `max_tokens=<${defaultMaxTokens}> | using default maxTokens, which is subject to change | set maxTokens explicitly to pin the value`
|
||||
}
|
||||
@@ -22,11 +22,9 @@ import type { GoogleModelConfig, GoogleModelOptions, GoogleStreamState } from '.
|
||||
export type { GoogleModelConfig, GoogleModelOptions }
|
||||
import { classifyGoogleError } from './errors.js'
|
||||
import { formatMessages, mapChunkToEvents } from './adapters.js'
|
||||
|
||||
/**
|
||||
* Default Gemini model ID.
|
||||
*/
|
||||
const DEFAULT_GEMINI_MODEL_ID = 'gemini-2.5-flash'
|
||||
import { MODEL_DEFAULTS, defaultModelWarningMessage } from '../defaults.js'
|
||||
import { warnOnce } from '../../logging/warn-once.js'
|
||||
import { logger } from '../../logging/logger.js'
|
||||
|
||||
/**
|
||||
* Google model provider implementation.
|
||||
@@ -94,6 +92,10 @@ export class GoogleModel extends Model<GoogleModelConfig> {
|
||||
|
||||
this._config = modelConfig
|
||||
|
||||
if (modelConfig.modelId === undefined) {
|
||||
warnOnce(logger, defaultModelWarningMessage(MODEL_DEFAULTS.gemini.modelId))
|
||||
}
|
||||
|
||||
if (client) {
|
||||
this._client = client
|
||||
} else {
|
||||
@@ -296,7 +298,7 @@ export class GoogleModel extends Model<GoogleModelConfig> {
|
||||
}
|
||||
|
||||
return {
|
||||
model: this._config.modelId ?? DEFAULT_GEMINI_MODEL_ID,
|
||||
model: this._config.modelId ?? MODEL_DEFAULTS.gemini.modelId,
|
||||
contents,
|
||||
config,
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ import type { ModelStreamEvent } from '../models/streaming.js'
|
||||
import { ContextWindowOverflowError, ModelThrottledError } from '../errors.js'
|
||||
import type { ChatCompletionContentPartText } from 'openai/resources/index.mjs'
|
||||
import { logger } from '../logging/logger.js'
|
||||
import { warnOnce } from '../logging/warn-once.js'
|
||||
import { MODEL_DEFAULTS, defaultModelWarningMessage } from './defaults.js'
|
||||
|
||||
/**
|
||||
* Supported OpenAI API types.
|
||||
@@ -26,8 +28,6 @@ import { logger } from '../logging/logger.js'
|
||||
*/
|
||||
export type OpenAIApi = 'chat'
|
||||
|
||||
const DEFAULT_OPENAI_MODEL_ID = 'gpt-5.4'
|
||||
|
||||
/**
|
||||
* Error message patterns that indicate context window overflow.
|
||||
* Used to detect when input exceeds the model's context window.
|
||||
@@ -263,6 +263,10 @@ export class OpenAIModel extends Model<OpenAIModelConfig> {
|
||||
// Initialize model config
|
||||
this._config = modelConfig
|
||||
|
||||
if (modelConfig.modelId === undefined) {
|
||||
warnOnce(logger, defaultModelWarningMessage(MODEL_DEFAULTS.openai.modelId))
|
||||
}
|
||||
|
||||
// Use provided client or create a new one
|
||||
if (client) {
|
||||
this._client = client
|
||||
@@ -466,7 +470,7 @@ export class OpenAIModel extends Model<OpenAIModelConfig> {
|
||||
): OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming {
|
||||
// Start with required fields
|
||||
const request: OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming = {
|
||||
model: this._config.modelId ?? DEFAULT_OPENAI_MODEL_ID,
|
||||
model: this._config.modelId ?? MODEL_DEFAULTS.openai.modelId,
|
||||
messages: [] as OpenAI.Chat.Completions.ChatCompletionMessageParam[],
|
||||
stream: true,
|
||||
stream_options: { include_usage: true },
|
||||
|
||||
Reference in New Issue
Block a user