@oai2lmapi/opencode-provider 0.3.1 → 0.3.2-prerelease.20260122071058.f95b2ba

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.
package/dist/index.js CHANGED
@@ -85,6 +85,25 @@ function resolveApiKey(config) {
85
85
  }
86
86
  return process.env["OAI2LM_API_KEY"];
87
87
  }
88
+ function findModelOverride(modelId, overrides) {
89
+ if (!overrides) {
90
+ return void 0;
91
+ }
92
+ if (overrides[modelId]) {
93
+ return overrides[modelId];
94
+ }
95
+ for (const [pattern, override] of Object.entries(overrides)) {
96
+ if (pattern.includes("*") || pattern.includes("?")) {
97
+ const regex = new RegExp(
98
+ "^" + pattern.replace(/\*/g, ".*").replace(/\?/g, ".") + "$"
99
+ );
100
+ if (regex.test(modelId)) {
101
+ return override;
102
+ }
103
+ }
104
+ }
105
+ return void 0;
106
+ }
88
107
 
89
108
  // src/discover.ts
90
109
  async function discoverModels(baseURL, apiKey, headers) {
@@ -941,104 +960,180 @@ ${result}
941
960
  }
942
961
 
943
962
  // src/plugin.ts
944
- function generateModelsConfig(models, providerName = "custom-provider") {
963
+ function generateModelsConfig(models, providerName = "custom-provider", config, modelOverrides) {
945
964
  const modelsConfig = {};
946
965
  for (const model of models) {
947
- const metadata = getModelMetadataFromPatterns2(model.id);
948
- const merged = mergeMetadata(model.metadata, metadata);
949
- modelsConfig[model.id] = {
966
+ const patternMetadata = getModelMetadataFromPatterns2(model.id);
967
+ const metadata = mergeMetadata(model.metadata, patternMetadata);
968
+ const override = findModelOverride(model.id, modelOverrides);
969
+ if (override) {
970
+ if (override.maxInputTokens !== void 0) {
971
+ metadata.maxInputTokens = override.maxInputTokens;
972
+ }
973
+ if (override.maxOutputTokens !== void 0) {
974
+ metadata.maxOutputTokens = override.maxOutputTokens;
975
+ }
976
+ if (override.supportsToolCalling !== void 0) {
977
+ metadata.supportsToolCalling = override.supportsToolCalling;
978
+ }
979
+ if (override.supportsImageInput !== void 0) {
980
+ metadata.supportsImageInput = override.supportsImageInput;
981
+ }
982
+ }
983
+ const modelConfig = {
950
984
  name: model.name || model.id,
951
- tool_call: merged.supportsToolCalling ?? true,
952
- attachment: merged.supportsImageInput ?? false,
985
+ tool_call: metadata.supportsToolCalling ?? true,
986
+ attachment: metadata.supportsImageInput ?? false,
953
987
  limit: {
954
- context: merged.maxInputTokens || 128e3,
955
- output: merged.maxOutputTokens || 16384
988
+ context: metadata.maxInputTokens || 128e3,
989
+ output: metadata.maxOutputTokens || 16384
956
990
  }
957
991
  };
992
+ if (override) {
993
+ const options = {};
994
+ if (override.usePromptBasedToolCalling !== void 0) {
995
+ options.usePromptBasedToolCalling = override.usePromptBasedToolCalling;
996
+ }
997
+ if (override.trimXmlToolParameterWhitespace !== void 0) {
998
+ options.trimXmlToolParameterWhitespace = override.trimXmlToolParameterWhitespace;
999
+ }
1000
+ if (override.thinkingLevel !== void 0) {
1001
+ options.thinkingLevel = override.thinkingLevel;
1002
+ }
1003
+ if (override.temperature !== void 0) {
1004
+ options.temperature = override.temperature;
1005
+ }
1006
+ if (Object.keys(options).length > 0) {
1007
+ modelConfig.options = options;
1008
+ }
1009
+ }
1010
+ modelsConfig[model.id] = modelConfig;
1011
+ }
1012
+ const providerOptions = {};
1013
+ if (config?.baseURL) {
1014
+ providerOptions.baseURL = config.baseURL;
1015
+ } else {
1016
+ providerOptions.baseURL = "YOUR_API_BASE_URL";
958
1017
  }
959
- const config = {
1018
+ if (config?.apiKey) {
1019
+ if (config.apiKey.startsWith("{env:")) {
1020
+ providerOptions.apiKey = config.apiKey;
1021
+ } else {
1022
+ providerOptions.apiKey = "{env:YOUR_API_KEY_ENV}";
1023
+ }
1024
+ } else {
1025
+ providerOptions.apiKey = "{env:YOUR_API_KEY_ENV}";
1026
+ }
1027
+ if (config?.headers && Object.keys(config.headers).length > 0) {
1028
+ providerOptions.headers = config.headers;
1029
+ }
1030
+ const outputConfig = {
960
1031
  provider: {
961
1032
  [providerName]: {
962
- name: providerName,
1033
+ name: config?.displayName || providerName,
963
1034
  npm: "@oai2lmapi/opencode-provider",
964
- options: {
965
- baseURL: "YOUR_API_BASE_URL",
966
- apiKey: "{env:YOUR_API_KEY_ENV}"
967
- },
1035
+ options: providerOptions,
968
1036
  models: modelsConfig
969
1037
  }
970
1038
  }
971
1039
  };
972
- return JSON.stringify(config, null, 2);
1040
+ return JSON.stringify(outputConfig, null, 2);
973
1041
  }
974
- function formatModelsForDisplay(models, providerName) {
1042
+ function formatModelsForDisplay(models, providerName, config) {
975
1043
  const lines = [
976
1044
  `# Discovered ${models.length} models`,
977
- "",
978
- "## Quick Setup",
979
- "",
980
- "Add the following to your opencode.json:",
981
- "",
982
- "```json",
983
- generateModelsConfig(models, providerName),
984
- "```",
985
- "",
986
- "## Model Details",
987
1045
  ""
988
1046
  ];
1047
+ if (config?.baseURL) {
1048
+ lines.push(`**API Endpoint**: ${config.baseURL}`);
1049
+ lines.push("");
1050
+ }
1051
+ lines.push("## Quick Setup");
1052
+ lines.push("");
1053
+ lines.push("Add the following to your opencode.json:");
1054
+ lines.push("");
1055
+ lines.push("```json");
1056
+ lines.push(
1057
+ generateModelsConfig(models, providerName, config, config?.modelOverrides)
1058
+ );
1059
+ lines.push("```");
1060
+ lines.push("");
1061
+ lines.push("## Model Details");
1062
+ lines.push("");
989
1063
  for (const model of models) {
990
- const metadata = getModelMetadataFromPatterns2(model.id);
991
- const merged = mergeMetadata(model.metadata, metadata);
1064
+ const patternMetadata = getModelMetadataFromPatterns2(model.id);
1065
+ const metadata = mergeMetadata(model.metadata, patternMetadata);
1066
+ const override = findModelOverride(model.id, config?.modelOverrides);
1067
+ if (override) {
1068
+ if (override.maxInputTokens !== void 0) {
1069
+ metadata.maxInputTokens = override.maxInputTokens;
1070
+ }
1071
+ if (override.maxOutputTokens !== void 0) {
1072
+ metadata.maxOutputTokens = override.maxOutputTokens;
1073
+ }
1074
+ if (override.supportsToolCalling !== void 0) {
1075
+ metadata.supportsToolCalling = override.supportsToolCalling;
1076
+ }
1077
+ if (override.supportsImageInput !== void 0) {
1078
+ metadata.supportsImageInput = override.supportsImageInput;
1079
+ }
1080
+ }
992
1081
  lines.push(`### ${model.id}`);
993
1082
  if (model.name && model.name !== model.id) {
994
1083
  lines.push(`- Name: ${model.name}`);
995
1084
  }
996
- lines.push(`- Context: ${merged.maxInputTokens || "unknown"} tokens`);
997
- lines.push(`- Output: ${merged.maxOutputTokens || "unknown"} tokens`);
998
- lines.push(`- Tool Calling: ${merged.supportsToolCalling ? "Yes" : "No"}`);
999
- lines.push(`- Vision: ${merged.supportsImageInput ? "Yes" : "No"}`);
1085
+ lines.push(`- Context: ${metadata.maxInputTokens || "unknown"} tokens`);
1086
+ lines.push(`- Output: ${metadata.maxOutputTokens || "unknown"} tokens`);
1087
+ lines.push(`- Tool Calling: ${metadata.supportsToolCalling ? "Yes" : "No"}`);
1088
+ lines.push(`- Vision: ${metadata.supportsImageInput ? "Yes" : "No"}`);
1089
+ if (override?.usePromptBasedToolCalling) {
1090
+ lines.push(`- Uses Prompt-Based Tool Calling: Yes`);
1091
+ }
1000
1092
  lines.push("");
1001
1093
  }
1002
1094
  return lines.join("\n");
1003
1095
  }
1004
1096
  async function oai2lmPlugin(_input) {
1097
+ const savedConfig = loadConfig();
1005
1098
  return {
1006
1099
  tool: {
1007
1100
  oai2lm_discover: {
1008
1101
  description: `Discover available models from an OpenAI-compatible API and generate opencode.json configuration.
1009
1102
 
1010
1103
  This tool will:
1011
- 1. Connect to the specified API endpoint
1104
+ 1. Connect to the specified API endpoint (or use oai2lm.json config)
1012
1105
  2. Fetch all available models
1013
1106
  3. Enrich them with metadata (token limits, capabilities)
1014
- 4. Generate ready-to-use opencode.json configuration
1107
+ 4. Apply any model overrides from oai2lm.json
1108
+ 5. Generate ready-to-use opencode.json configuration
1015
1109
 
1016
- Use this when you want to add a new OpenAI-compatible provider to OpenCode.`,
1110
+ Configuration from oai2lm.json (~/.local/share/opencode/oai2lm.json) is automatically loaded.
1111
+ You only need to provide arguments if you want to override the config file settings.`,
1017
1112
  parameters: {
1018
1113
  type: "object",
1019
1114
  properties: {
1020
1115
  baseURL: {
1021
1116
  type: "string",
1022
- description: "Base URL of the OpenAI-compatible API (e.g., https://api.example.com/v1). If not provided, will try to load from oai2lm.json config."
1117
+ description: "Base URL of the OpenAI-compatible API (e.g., https://api.example.com/v1). If not provided, uses oai2lm.json config."
1023
1118
  },
1024
1119
  apiKey: {
1025
1120
  type: "string",
1026
- description: "API key for authentication. If not provided, will try to load from config or environment."
1121
+ description: "API key for authentication. If not provided, uses oai2lm.json config or environment."
1027
1122
  },
1028
1123
  providerName: {
1029
1124
  type: "string",
1030
- description: "Name for the provider in opencode.json (e.g., 'my-api'). Defaults to 'custom-provider'."
1125
+ description: "Name for the provider in opencode.json (e.g., 'my-api'). Defaults to config name or 'custom-provider'."
1031
1126
  },
1032
1127
  filter: {
1033
1128
  type: "string",
1034
- description: "Optional regex pattern to filter models (e.g., 'gpt|claude')"
1129
+ description: "Optional regex pattern to filter models (e.g., 'gpt|claude')."
1035
1130
  }
1036
1131
  },
1037
1132
  required: []
1038
1133
  },
1039
1134
  execute: async (args) => {
1040
1135
  try {
1041
- const config = loadConfig();
1136
+ const config = loadConfig() || savedConfig;
1042
1137
  const baseURL = args.baseURL || config?.baseURL || "";
1043
1138
  const apiKey = args.apiKey || (config ? resolveApiKey(config) : "") || "";
1044
1139
  const providerName = args.providerName || config?.name || "custom-provider";
@@ -1046,12 +1141,18 @@ Use this when you want to add a new OpenAI-compatible provider to OpenCode.`,
1046
1141
  if (!baseURL) {
1047
1142
  return `Error: No baseURL provided. Either:
1048
1143
  1. Pass it as an argument: oai2lm_discover(baseURL: "https://api.example.com/v1")
1049
- 2. Or create an oai2lm.json config file with baseURL
1144
+ 2. Or create an oai2lm.json config file at ~/.local/share/opencode/oai2lm.json
1050
1145
 
1051
1146
  Example oai2lm.json:
1052
1147
  {
1053
1148
  "baseURL": "https://api.example.com/v1",
1054
- "apiKey": "your-api-key"
1149
+ "apiKey": "{env:MY_API_KEY}",
1150
+ "name": "my-api",
1151
+ "modelOverrides": {
1152
+ "gpt-4*": {
1153
+ "supportsImageInput": true
1154
+ }
1155
+ }
1055
1156
  }`;
1056
1157
  }
1057
1158
  let models = await discoverModels(baseURL, apiKey, config?.headers);
@@ -1062,7 +1163,7 @@ Example oai2lm.json:
1062
1163
  if (models.length === 0) {
1063
1164
  return `No models found at ${baseURL}/models. Check your API key and endpoint.`;
1064
1165
  }
1065
- return formatModelsForDisplay(models, providerName);
1166
+ return formatModelsForDisplay(models, providerName, config);
1066
1167
  } catch (error) {
1067
1168
  const message = error instanceof Error ? error.message : String(error);
1068
1169
  return `Error discovering models: ${message}`;
@@ -1073,48 +1174,291 @@ Example oai2lm.json:
1073
1174
  };
1074
1175
  }
1075
1176
 
1076
- // src/index.ts
1077
- var modelCache = /* @__PURE__ */ new WeakMap();
1078
- function findModelOverride(modelId, overrides) {
1079
- if (!overrides) {
1080
- return void 0;
1177
+ // src/enhancedModel.ts
1178
+ var idCounter = 0;
1179
+ function generateId() {
1180
+ return `id_${Date.now()}_${++idCounter}`;
1181
+ }
1182
+ var EnhancedLanguageModel = class {
1183
+ specificationVersion = "v2";
1184
+ modelId;
1185
+ baseModel;
1186
+ override;
1187
+ constructor(baseModel, modelId, override) {
1188
+ this.baseModel = baseModel;
1189
+ this.modelId = modelId;
1190
+ this.override = override;
1081
1191
  }
1082
- if (overrides[modelId]) {
1083
- return overrides[modelId];
1192
+ get provider() {
1193
+ return this.baseModel.provider;
1084
1194
  }
1085
- for (const [pattern, override] of Object.entries(overrides)) {
1086
- if (pattern.includes("*") || pattern.includes("?")) {
1087
- const regex = new RegExp(
1088
- "^" + pattern.replace(/\*/g, ".*").replace(/\?/g, ".") + "$"
1089
- );
1090
- {
1195
+ get supportedUrls() {
1196
+ return this.baseModel.supportedUrls;
1197
+ }
1198
+ /**
1199
+ * Generate text (non-streaming)
1200
+ */
1201
+ async doGenerate(options) {
1202
+ let modifiedOptions = { ...options };
1203
+ const toolNames = [];
1204
+ if (this.override?.usePromptBasedToolCalling && options.tools && options.tools.length > 0) {
1205
+ const result2 = this.applyPromptBasedToolCalling(modifiedOptions);
1206
+ modifiedOptions = result2.options;
1207
+ toolNames.push(...result2.toolNames);
1208
+ }
1209
+ const result = await this.baseModel.doGenerate(modifiedOptions);
1210
+ if (this.override?.usePromptBasedToolCalling && toolNames.length > 0) {
1211
+ return this.processResultForToolCalls(result, toolNames);
1212
+ }
1213
+ return result;
1214
+ }
1215
+ /**
1216
+ * Stream text
1217
+ */
1218
+ async doStream(options) {
1219
+ let modifiedOptions = { ...options };
1220
+ const toolNames = [];
1221
+ if (this.override?.usePromptBasedToolCalling && options.tools && options.tools.length > 0) {
1222
+ const result2 = this.applyPromptBasedToolCalling(modifiedOptions);
1223
+ modifiedOptions = result2.options;
1224
+ toolNames.push(...result2.toolNames);
1225
+ }
1226
+ const result = await this.baseModel.doStream(modifiedOptions);
1227
+ if (this.override?.usePromptBasedToolCalling && toolNames.length > 0) {
1228
+ return this.processStreamForToolCalls(result, toolNames);
1229
+ }
1230
+ return result;
1231
+ }
1232
+ /**
1233
+ * Apply prompt-based tool calling by converting tools to system prompt
1234
+ */
1235
+ applyPromptBasedToolCalling(options) {
1236
+ if (!options.tools || options.tools.length === 0) {
1237
+ return { options, toolNames: [] };
1238
+ }
1239
+ const functionTools = options.tools.filter(
1240
+ (tool) => tool.type === "function"
1241
+ );
1242
+ if (functionTools.length === 0) {
1243
+ return { options, toolNames: [] };
1244
+ }
1245
+ const toolDefinitions = functionTools.map((tool) => ({
1246
+ type: "function",
1247
+ name: tool.name,
1248
+ description: tool.description,
1249
+ // V2 uses inputSchema instead of parameters
1250
+ parameters: tool.inputSchema
1251
+ }));
1252
+ const toolNames = toolDefinitions.map((t) => t.name);
1253
+ const toolPrompt = generateXmlToolPrompt(toolDefinitions);
1254
+ const modifiedPrompt = options.prompt.map((msg) => {
1255
+ if (msg.role === "system") {
1256
+ const content = msg.content;
1257
+ return {
1258
+ ...msg,
1259
+ content: content + "\n\n" + toolPrompt
1260
+ };
1091
1261
  }
1092
- if (regex.test(modelId)) {
1093
- return override;
1262
+ return msg;
1263
+ });
1264
+ const hasSystemMessage = modifiedPrompt.some((m) => m.role === "system");
1265
+ if (!hasSystemMessage) {
1266
+ modifiedPrompt.unshift({
1267
+ role: "system",
1268
+ content: toolPrompt
1269
+ });
1270
+ }
1271
+ return {
1272
+ options: {
1273
+ ...options,
1274
+ prompt: modifiedPrompt,
1275
+ tools: void 0,
1276
+ toolChoice: void 0
1277
+ },
1278
+ toolNames
1279
+ };
1280
+ }
1281
+ /**
1282
+ * Process non-streaming result for XML tool calls
1283
+ */
1284
+ processResultForToolCalls(result, toolNames) {
1285
+ const content = result.content;
1286
+ const textContentIndex = content.findIndex((c) => c.type === "text");
1287
+ if (textContentIndex === -1 || !content[textContentIndex].text) {
1288
+ return result;
1289
+ }
1290
+ const text = content[textContentIndex].text;
1291
+ const xmlToolCalls = parseXmlToolCalls(text, toolNames, {
1292
+ trimParameterWhitespace: this.override?.trimXmlToolParameterWhitespace ?? false
1293
+ });
1294
+ if (xmlToolCalls.length === 0) {
1295
+ return result;
1296
+ }
1297
+ const cleanedText = this.removeXmlToolCallsFromText(text, toolNames);
1298
+ const newContent = [];
1299
+ if (cleanedText.trim()) {
1300
+ newContent.push({
1301
+ type: "text",
1302
+ text: cleanedText
1303
+ });
1304
+ }
1305
+ for (const tc of xmlToolCalls) {
1306
+ newContent.push({
1307
+ type: "tool-call",
1308
+ toolCallId: tc.id,
1309
+ toolName: tc.name,
1310
+ input: JSON.stringify(tc.arguments)
1311
+ });
1312
+ }
1313
+ for (const c of content) {
1314
+ if (c.type !== "text") {
1315
+ newContent.push(c);
1094
1316
  }
1095
1317
  }
1318
+ return {
1319
+ ...result,
1320
+ content: newContent,
1321
+ finishReason: "tool-calls"
1322
+ };
1096
1323
  }
1097
- return void 0;
1324
+ /**
1325
+ * Process streaming result for XML tool calls
1326
+ *
1327
+ * This wraps the stream to accumulate text and parse tool calls at the end.
1328
+ */
1329
+ processStreamForToolCalls(result, toolNames) {
1330
+ const originalStream = result.stream;
1331
+ const override = this.override;
1332
+ const self = this;
1333
+ const transformedStream = new ReadableStream({
1334
+ async start(controller) {
1335
+ let accumulatedText = "";
1336
+ const bufferedParts = [];
1337
+ try {
1338
+ const reader = originalStream.getReader();
1339
+ while (true) {
1340
+ const { done, value } = await reader.read();
1341
+ if (done) {
1342
+ break;
1343
+ }
1344
+ const part = value;
1345
+ if (part.type === "text-delta") {
1346
+ accumulatedText += part.delta;
1347
+ }
1348
+ bufferedParts.push(part);
1349
+ }
1350
+ const xmlToolCalls = parseXmlToolCalls(accumulatedText, toolNames, {
1351
+ trimParameterWhitespace: override?.trimXmlToolParameterWhitespace ?? false
1352
+ });
1353
+ if (xmlToolCalls.length > 0) {
1354
+ const cleanedText = self.removeXmlToolCallsFromText(
1355
+ accumulatedText,
1356
+ toolNames
1357
+ );
1358
+ if (cleanedText.trim()) {
1359
+ controller.enqueue({
1360
+ type: "text-delta",
1361
+ id: generateId(),
1362
+ delta: cleanedText
1363
+ });
1364
+ }
1365
+ for (const tc of xmlToolCalls) {
1366
+ controller.enqueue({
1367
+ type: "tool-call",
1368
+ toolCallId: tc.id,
1369
+ toolName: tc.name,
1370
+ input: JSON.stringify(tc.arguments)
1371
+ });
1372
+ }
1373
+ controller.enqueue({
1374
+ type: "finish",
1375
+ finishReason: "tool-calls",
1376
+ usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 }
1377
+ });
1378
+ } else {
1379
+ for (const part of bufferedParts) {
1380
+ controller.enqueue(part);
1381
+ }
1382
+ }
1383
+ controller.close();
1384
+ } catch (error) {
1385
+ controller.error(error);
1386
+ }
1387
+ }
1388
+ });
1389
+ return {
1390
+ ...result,
1391
+ stream: transformedStream
1392
+ };
1393
+ }
1394
+ /**
1395
+ * Remove XML tool call blocks from text
1396
+ */
1397
+ removeXmlToolCallsFromText(text, toolNames) {
1398
+ let result = text;
1399
+ for (const toolName of toolNames) {
1400
+ const regex = new RegExp(
1401
+ `<${this.escapeRegex(toolName)}>[\\s\\S]*?<\\/${this.escapeRegex(toolName)}>`,
1402
+ "g"
1403
+ );
1404
+ result = result.replace(regex, "");
1405
+ }
1406
+ return result.trim();
1407
+ }
1408
+ /**
1409
+ * Escape special regex characters
1410
+ */
1411
+ escapeRegex(str) {
1412
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1413
+ }
1414
+ };
1415
+ function createEnhancedModel(baseModel, modelId, override) {
1416
+ if (override?.usePromptBasedToolCalling) {
1417
+ return new EnhancedLanguageModel(baseModel, modelId, override);
1418
+ }
1419
+ return baseModel;
1098
1420
  }
1421
+
1422
+ // src/index.ts
1423
+ var modelCache = /* @__PURE__ */ new WeakMap();
1099
1424
  function mergeWithConfig(options, config) {
1100
- if (!config) {
1101
- return options;
1425
+ const baseModelOverrides = config?.modelOverrides ?? {};
1426
+ const optionModelOverrides = options.modelOverrides ?? {};
1427
+ const modelLevelOverrides = {};
1428
+ if (options.models) {
1429
+ for (const [modelId, modelConfig] of Object.entries(options.models)) {
1430
+ if (modelConfig.options) {
1431
+ modelLevelOverrides[modelId] = modelConfig.options;
1432
+ }
1433
+ }
1434
+ }
1435
+ const mergedModelOverrides = {
1436
+ ...baseModelOverrides,
1437
+ ...optionModelOverrides
1438
+ };
1439
+ for (const [modelId, override] of Object.entries(modelLevelOverrides)) {
1440
+ mergedModelOverrides[modelId] = {
1441
+ ...mergedModelOverrides[modelId],
1442
+ ...override
1443
+ };
1444
+ }
1445
+ let apiKey = options.apiKey;
1446
+ if (!apiKey && config) {
1447
+ apiKey = resolveApiKey(config);
1102
1448
  }
1103
1449
  return {
1104
1450
  // Options take precedence over config
1105
- baseURL: options.baseURL || config.baseURL || "",
1106
- apiKey: options.apiKey || resolveApiKey(config),
1107
- name: options.name || config.name,
1451
+ baseURL: options.baseURL || config?.baseURL || "",
1452
+ apiKey,
1453
+ name: options.name || config?.name,
1108
1454
  headers: {
1109
- ...config.headers,
1455
+ ...config?.headers,
1110
1456
  ...options.headers
1111
1457
  },
1112
- modelFilter: options.modelFilter || config.modelFilter,
1113
- modelOverrides: {
1114
- ...config.modelOverrides,
1115
- ...options.modelOverrides
1116
- },
1117
- useConfigFile: options.useConfigFile
1458
+ modelFilter: options.modelFilter || config?.modelFilter,
1459
+ modelOverrides: mergedModelOverrides,
1460
+ useConfigFile: options.useConfigFile,
1461
+ models: options.models
1118
1462
  };
1119
1463
  }
1120
1464
  function createOai2lm(options) {
@@ -1125,8 +1469,9 @@ function createOai2lm(options) {
1125
1469
  "baseURL is required. Provide it in options or in oai2lm.json config file."
1126
1470
  );
1127
1471
  }
1472
+ const baseURL = mergedOptions.baseURL;
1128
1473
  const baseProvider = createOpenAICompatible({
1129
- baseURL: mergedOptions.baseURL.replace(/\/+$/, ""),
1474
+ baseURL: baseURL.replace(/\/+$/, ""),
1130
1475
  name: mergedOptions.name || "oai2lm",
1131
1476
  apiKey: mergedOptions.apiKey,
1132
1477
  headers: mergedOptions.headers
@@ -1135,7 +1480,7 @@ function createOai2lm(options) {
1135
1480
  async function discoverAndCache() {
1136
1481
  const apiKey = mergedOptions.apiKey || "";
1137
1482
  const models = await discoverModels(
1138
- mergedOptions.baseURL,
1483
+ baseURL,
1139
1484
  apiKey,
1140
1485
  mergedOptions.headers
1141
1486
  );
@@ -1182,11 +1527,24 @@ function createOai2lm(options) {
1182
1527
  }
1183
1528
  return cache;
1184
1529
  }
1530
+ function getOverride(modelId) {
1531
+ return findModelOverride(modelId, mergedOptions.modelOverrides);
1532
+ }
1185
1533
  const provider = function(modelId) {
1186
- return baseProvider(modelId);
1534
+ const baseModel = baseProvider(modelId);
1535
+ const override = getOverride(modelId);
1536
+ return createEnhancedModel(baseModel, modelId, override);
1537
+ };
1538
+ provider.languageModel = (modelId) => {
1539
+ const baseModel = baseProvider.languageModel(modelId);
1540
+ const override = getOverride(modelId);
1541
+ return createEnhancedModel(baseModel, modelId, override);
1542
+ };
1543
+ provider.chatModel = (modelId) => {
1544
+ const baseModel = baseProvider.chatModel(modelId);
1545
+ const override = getOverride(modelId);
1546
+ return createEnhancedModel(baseModel, modelId, override);
1187
1547
  };
1188
- provider.languageModel = (modelId) => baseProvider.languageModel(modelId);
1189
- provider.chatModel = (modelId) => baseProvider.chatModel(modelId);
1190
1548
  provider.completionModel = (modelId) => baseProvider.completionModel(modelId);
1191
1549
  provider.textEmbeddingModel = (modelId) => baseProvider.textEmbeddingModel(modelId);
1192
1550
  provider.imageModel = (modelId) => baseProvider.imageModel(modelId);
@@ -1205,6 +1563,8 @@ function createOai2lm(options) {
1205
1563
  }
1206
1564
  var index_default = createOai2lm;
1207
1565
  export {
1566
+ EnhancedLanguageModel,
1567
+ createEnhancedModel,
1208
1568
  createOai2lm,
1209
1569
  index_default as default,
1210
1570
  discoverModels,