@promptbook/ollama 0.101.0-14 → 0.101.0-15

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/umd/index.umd.js CHANGED
@@ -25,7 +25,7 @@
25
25
  * @generated
26
26
  * @see https://github.com/webgptorg/promptbook
27
27
  */
28
- const PROMPTBOOK_ENGINE_VERSION = '0.101.0-14';
28
+ const PROMPTBOOK_ENGINE_VERSION = '0.101.0-15';
29
29
  /**
30
30
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
31
31
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -1919,6 +1919,62 @@
1919
1919
  return replacedTemplates;
1920
1920
  }
1921
1921
 
1922
+ /**
1923
+ * Parses an OpenAI error message to identify which parameter is unsupported
1924
+ *
1925
+ * @param errorMessage The error message from OpenAI API
1926
+ * @returns The parameter name that is unsupported, or null if not an unsupported parameter error
1927
+ * @private utility of LLM Tools
1928
+ */
1929
+ function parseUnsupportedParameterError(errorMessage) {
1930
+ // Pattern to match "Unsupported value: 'parameter' does not support ..."
1931
+ const unsupportedValueMatch = errorMessage.match(/Unsupported value:\s*'([^']+)'\s*does not support/i);
1932
+ if (unsupportedValueMatch === null || unsupportedValueMatch === void 0 ? void 0 : unsupportedValueMatch[1]) {
1933
+ return unsupportedValueMatch[1];
1934
+ }
1935
+ // Pattern to match "'parameter' of type ... is not supported with this model"
1936
+ const parameterTypeMatch = errorMessage.match(/'([^']+)'\s*of type.*is not supported with this model/i);
1937
+ if (parameterTypeMatch === null || parameterTypeMatch === void 0 ? void 0 : parameterTypeMatch[1]) {
1938
+ return parameterTypeMatch[1];
1939
+ }
1940
+ return null;
1941
+ }
1942
+ /**
1943
+ * Creates a copy of model requirements with the specified parameter removed
1944
+ *
1945
+ * @param modelRequirements Original model requirements
1946
+ * @param unsupportedParameter The parameter to remove
1947
+ * @returns New model requirements without the unsupported parameter
1948
+ * @private utility of LLM Tools
1949
+ */
1950
+ function removeUnsupportedModelRequirement(modelRequirements, unsupportedParameter) {
1951
+ const newRequirements = { ...modelRequirements };
1952
+ // Map of parameter names that might appear in error messages to ModelRequirements properties
1953
+ const parameterMap = {
1954
+ 'temperature': 'temperature',
1955
+ 'max_tokens': 'maxTokens',
1956
+ 'maxTokens': 'maxTokens',
1957
+ 'seed': 'seed',
1958
+ };
1959
+ const propertyToRemove = parameterMap[unsupportedParameter];
1960
+ if (propertyToRemove && propertyToRemove in newRequirements) {
1961
+ delete newRequirements[propertyToRemove];
1962
+ }
1963
+ return newRequirements;
1964
+ }
1965
+ /**
1966
+ * Checks if an error is an "Unsupported value" error from OpenAI
1967
+ * @param error The error to check
1968
+ * @returns true if this is an unsupported parameter error
1969
+ * @private utility of LLM Tools
1970
+ */
1971
+ function isUnsupportedParameterError(error) {
1972
+ const errorMessage = error.message.toLowerCase();
1973
+ return errorMessage.includes('unsupported value:') ||
1974
+ errorMessage.includes('is not supported with this model') ||
1975
+ errorMessage.includes('does not support');
1976
+ }
1977
+
1922
1978
  /**
1923
1979
  * Execution Tools for calling OpenAI API or other OpenAI compatible provider
1924
1980
  *
@@ -1936,6 +1992,10 @@
1936
1992
  * OpenAI API client.
1937
1993
  */
1938
1994
  this.client = null;
1995
+ /**
1996
+ * Tracks models and parameters that have already been retried to prevent infinite loops
1997
+ */
1998
+ this.retriedUnsupportedParameters = new Set();
1939
1999
  // TODO: Allow configuring rate limits via options
1940
2000
  this.limiter = new Bottleneck__default["default"]({
1941
2001
  minTime: 60000 / (this.options.maxRequestsPerMinute || DEFAULT_MAX_REQUESTS_PER_MINUTE),
@@ -1997,21 +2057,27 @@
1997
2057
  * Calls OpenAI compatible API to use a chat model.
1998
2058
  */
1999
2059
  async callChatModel(prompt) {
2060
+ return this.callChatModelWithRetry(prompt, prompt.modelRequirements);
2061
+ }
2062
+ /**
2063
+ * Internal method that handles parameter retry for chat model calls
2064
+ */
2065
+ async callChatModelWithRetry(prompt, currentModelRequirements) {
2000
2066
  var _a;
2001
2067
  if (this.options.isVerbose) {
2002
- console.info(`💬 ${this.title} callChatModel call`, { prompt });
2068
+ console.info(`💬 ${this.title} callChatModel call`, { prompt, currentModelRequirements });
2003
2069
  }
2004
- const { content, parameters, modelRequirements, format } = prompt;
2070
+ const { content, parameters, format } = prompt;
2005
2071
  const client = await this.getClient();
2006
2072
  // TODO: [☂] Use here more modelRequirements
2007
- if (modelRequirements.modelVariant !== 'CHAT') {
2073
+ if (currentModelRequirements.modelVariant !== 'CHAT') {
2008
2074
  throw new PipelineExecutionError('Use callChatModel only for CHAT variant');
2009
2075
  }
2010
- const modelName = modelRequirements.modelName || this.getDefaultChatModel().modelName;
2076
+ const modelName = currentModelRequirements.modelName || this.getDefaultChatModel().modelName;
2011
2077
  const modelSettings = {
2012
2078
  model: modelName,
2013
- max_tokens: modelRequirements.maxTokens,
2014
- temperature: modelRequirements.temperature,
2079
+ max_tokens: currentModelRequirements.maxTokens,
2080
+ temperature: currentModelRequirements.temperature,
2015
2081
  // <- TODO: [🈁] Use `seed` here AND/OR use is `isDeterministic` for entire execution tools
2016
2082
  // <- Note: [🧆]
2017
2083
  }; // <- TODO: [💩] Guard here types better
@@ -2026,12 +2092,12 @@
2026
2092
  const rawRequest = {
2027
2093
  ...modelSettings,
2028
2094
  messages: [
2029
- ...(modelRequirements.systemMessage === undefined
2095
+ ...(currentModelRequirements.systemMessage === undefined
2030
2096
  ? []
2031
2097
  : [
2032
2098
  {
2033
2099
  role: 'system',
2034
- content: modelRequirements.systemMessage,
2100
+ content: currentModelRequirements.systemMessage,
2035
2101
  },
2036
2102
  ]),
2037
2103
  {
@@ -2045,69 +2111,110 @@
2045
2111
  if (this.options.isVerbose) {
2046
2112
  console.info(colors__default["default"].bgWhite('rawRequest'), JSON.stringify(rawRequest, null, 4));
2047
2113
  }
2048
- const rawResponse = await this.limiter
2049
- .schedule(() => this.makeRequestWithRetry(() => client.chat.completions.create(rawRequest)))
2050
- .catch((error) => {
2051
- assertsError(error);
2114
+ try {
2115
+ const rawResponse = await this.limiter
2116
+ .schedule(() => this.makeRequestWithNetworkRetry(() => client.chat.completions.create(rawRequest)))
2117
+ .catch((error) => {
2118
+ assertsError(error);
2119
+ if (this.options.isVerbose) {
2120
+ console.info(colors__default["default"].bgRed('error'), error);
2121
+ }
2122
+ throw error;
2123
+ });
2052
2124
  if (this.options.isVerbose) {
2053
- console.info(colors__default["default"].bgRed('error'), error);
2125
+ console.info(colors__default["default"].bgWhite('rawResponse'), JSON.stringify(rawResponse, null, 4));
2054
2126
  }
2055
- throw error;
2056
- });
2057
- if (this.options.isVerbose) {
2058
- console.info(colors__default["default"].bgWhite('rawResponse'), JSON.stringify(rawResponse, null, 4));
2059
- }
2060
- const complete = $getCurrentDate();
2061
- if (!rawResponse.choices[0]) {
2062
- throw new PipelineExecutionError(`No choises from ${this.title}`);
2063
- }
2064
- if (rawResponse.choices.length > 1) {
2065
- // TODO: This should be maybe only warning
2066
- throw new PipelineExecutionError(`More than one choise from ${this.title}`);
2127
+ const complete = $getCurrentDate();
2128
+ if (!rawResponse.choices[0]) {
2129
+ throw new PipelineExecutionError(`No choises from ${this.title}`);
2130
+ }
2131
+ if (rawResponse.choices.length > 1) {
2132
+ // TODO: This should be maybe only warning
2133
+ throw new PipelineExecutionError(`More than one choise from ${this.title}`);
2134
+ }
2135
+ const resultContent = rawResponse.choices[0].message.content;
2136
+ const usage = this.computeUsage(content || '', resultContent || '', rawResponse);
2137
+ if (resultContent === null) {
2138
+ throw new PipelineExecutionError(`No response message from ${this.title}`);
2139
+ }
2140
+ return exportJson({
2141
+ name: 'promptResult',
2142
+ message: `Result of \`OpenAiCompatibleExecutionTools.callChatModel\``,
2143
+ order: [],
2144
+ value: {
2145
+ content: resultContent,
2146
+ modelName: rawResponse.model || modelName,
2147
+ timing: {
2148
+ start,
2149
+ complete,
2150
+ },
2151
+ usage,
2152
+ rawPromptContent,
2153
+ rawRequest,
2154
+ rawResponse,
2155
+ // <- [🗯]
2156
+ },
2157
+ });
2067
2158
  }
2068
- const resultContent = rawResponse.choices[0].message.content;
2069
- const usage = this.computeUsage(content || '', resultContent || '', rawResponse);
2070
- if (resultContent === null) {
2071
- throw new PipelineExecutionError(`No response message from ${this.title}`);
2159
+ catch (error) {
2160
+ assertsError(error);
2161
+ // Check if this is an unsupported parameter error
2162
+ if (!isUnsupportedParameterError(error)) {
2163
+ throw error;
2164
+ }
2165
+ // Parse which parameter is unsupported
2166
+ const unsupportedParameter = parseUnsupportedParameterError(error.message);
2167
+ if (!unsupportedParameter) {
2168
+ if (this.options.isVerbose) {
2169
+ console.warn(colors__default["default"].bgYellow('Warning'), 'Could not parse unsupported parameter from error:', error.message);
2170
+ }
2171
+ throw error;
2172
+ }
2173
+ // Create a unique key for this model + parameter combination to prevent infinite loops
2174
+ const retryKey = `${modelName}-${unsupportedParameter}`;
2175
+ if (this.retriedUnsupportedParameters.has(retryKey)) {
2176
+ // Already retried this parameter, throw the error
2177
+ if (this.options.isVerbose) {
2178
+ console.warn(colors__default["default"].bgRed('Error'), `Parameter '${unsupportedParameter}' for model '${modelName}' already retried once, throwing error:`, error.message);
2179
+ }
2180
+ throw error;
2181
+ }
2182
+ // Mark this parameter as retried
2183
+ this.retriedUnsupportedParameters.add(retryKey);
2184
+ // Log warning in verbose mode
2185
+ if (this.options.isVerbose) {
2186
+ console.warn(colors__default["default"].bgYellow('Warning'), `Removing unsupported parameter '${unsupportedParameter}' for model '${modelName}' and retrying request`);
2187
+ }
2188
+ // Remove the unsupported parameter and retry
2189
+ const modifiedModelRequirements = removeUnsupportedModelRequirement(currentModelRequirements, unsupportedParameter);
2190
+ return this.callChatModelWithRetry(prompt, modifiedModelRequirements);
2072
2191
  }
2073
- return exportJson({
2074
- name: 'promptResult',
2075
- message: `Result of \`OpenAiCompatibleExecutionTools.callChatModel\``,
2076
- order: [],
2077
- value: {
2078
- content: resultContent,
2079
- modelName: rawResponse.model || modelName,
2080
- timing: {
2081
- start,
2082
- complete,
2083
- },
2084
- usage,
2085
- rawPromptContent,
2086
- rawRequest,
2087
- rawResponse,
2088
- // <- [🗯]
2089
- },
2090
- });
2091
2192
  }
2092
2193
  /**
2093
2194
  * Calls OpenAI API to use a complete model.
2094
2195
  */
2095
2196
  async callCompletionModel(prompt) {
2197
+ return this.callCompletionModelWithRetry(prompt, prompt.modelRequirements);
2198
+ }
2199
+ /**
2200
+ * Internal method that handles parameter retry for completion model calls
2201
+ */
2202
+ async callCompletionModelWithRetry(prompt, currentModelRequirements) {
2096
2203
  var _a;
2097
2204
  if (this.options.isVerbose) {
2098
- console.info(`🖋 ${this.title} callCompletionModel call`, { prompt });
2205
+ console.info(`🖋 ${this.title} callCompletionModel call`, { prompt, currentModelRequirements });
2099
2206
  }
2100
- const { content, parameters, modelRequirements } = prompt;
2207
+ const { content, parameters } = prompt;
2101
2208
  const client = await this.getClient();
2102
2209
  // TODO: [☂] Use here more modelRequirements
2103
- if (modelRequirements.modelVariant !== 'COMPLETION') {
2210
+ if (currentModelRequirements.modelVariant !== 'COMPLETION') {
2104
2211
  throw new PipelineExecutionError('Use callCompletionModel only for COMPLETION variant');
2105
2212
  }
2106
- const modelName = modelRequirements.modelName || this.getDefaultCompletionModel().modelName;
2213
+ const modelName = currentModelRequirements.modelName || this.getDefaultCompletionModel().modelName;
2107
2214
  const modelSettings = {
2108
2215
  model: modelName,
2109
- max_tokens: modelRequirements.maxTokens,
2110
- temperature: modelRequirements.temperature,
2216
+ max_tokens: currentModelRequirements.maxTokens,
2217
+ temperature: currentModelRequirements.temperature,
2111
2218
  // <- TODO: [🈁] Use `seed` here AND/OR use is `isDeterministic` for entire execution tools
2112
2219
  // <- Note: [🧆]
2113
2220
  };
@@ -2121,46 +2228,81 @@
2121
2228
  if (this.options.isVerbose) {
2122
2229
  console.info(colors__default["default"].bgWhite('rawRequest'), JSON.stringify(rawRequest, null, 4));
2123
2230
  }
2124
- const rawResponse = await this.limiter
2125
- .schedule(() => this.makeRequestWithRetry(() => client.completions.create(rawRequest)))
2126
- .catch((error) => {
2127
- assertsError(error);
2231
+ try {
2232
+ const rawResponse = await this.limiter
2233
+ .schedule(() => this.makeRequestWithNetworkRetry(() => client.completions.create(rawRequest)))
2234
+ .catch((error) => {
2235
+ assertsError(error);
2236
+ if (this.options.isVerbose) {
2237
+ console.info(colors__default["default"].bgRed('error'), error);
2238
+ }
2239
+ throw error;
2240
+ });
2128
2241
  if (this.options.isVerbose) {
2129
- console.info(colors__default["default"].bgRed('error'), error);
2242
+ console.info(colors__default["default"].bgWhite('rawResponse'), JSON.stringify(rawResponse, null, 4));
2130
2243
  }
2131
- throw error;
2132
- });
2133
- if (this.options.isVerbose) {
2134
- console.info(colors__default["default"].bgWhite('rawResponse'), JSON.stringify(rawResponse, null, 4));
2135
- }
2136
- const complete = $getCurrentDate();
2137
- if (!rawResponse.choices[0]) {
2138
- throw new PipelineExecutionError(`No choises from ${this.title}`);
2244
+ const complete = $getCurrentDate();
2245
+ if (!rawResponse.choices[0]) {
2246
+ throw new PipelineExecutionError(`No choises from ${this.title}`);
2247
+ }
2248
+ if (rawResponse.choices.length > 1) {
2249
+ // TODO: This should be maybe only warning
2250
+ throw new PipelineExecutionError(`More than one choise from ${this.title}`);
2251
+ }
2252
+ const resultContent = rawResponse.choices[0].text;
2253
+ const usage = this.computeUsage(content || '', resultContent || '', rawResponse);
2254
+ return exportJson({
2255
+ name: 'promptResult',
2256
+ message: `Result of \`OpenAiCompatibleExecutionTools.callCompletionModel\``,
2257
+ order: [],
2258
+ value: {
2259
+ content: resultContent,
2260
+ modelName: rawResponse.model || modelName,
2261
+ timing: {
2262
+ start,
2263
+ complete,
2264
+ },
2265
+ usage,
2266
+ rawPromptContent,
2267
+ rawRequest,
2268
+ rawResponse,
2269
+ // <- [🗯]
2270
+ },
2271
+ });
2139
2272
  }
2140
- if (rawResponse.choices.length > 1) {
2141
- // TODO: This should be maybe only warning
2142
- throw new PipelineExecutionError(`More than one choise from ${this.title}`);
2273
+ catch (error) {
2274
+ assertsError(error);
2275
+ // Check if this is an unsupported parameter error
2276
+ if (!isUnsupportedParameterError(error)) {
2277
+ throw error;
2278
+ }
2279
+ // Parse which parameter is unsupported
2280
+ const unsupportedParameter = parseUnsupportedParameterError(error.message);
2281
+ if (!unsupportedParameter) {
2282
+ if (this.options.isVerbose) {
2283
+ console.warn(colors__default["default"].bgYellow('Warning'), 'Could not parse unsupported parameter from error:', error.message);
2284
+ }
2285
+ throw error;
2286
+ }
2287
+ // Create a unique key for this model + parameter combination to prevent infinite loops
2288
+ const retryKey = `${modelName}-${unsupportedParameter}`;
2289
+ if (this.retriedUnsupportedParameters.has(retryKey)) {
2290
+ // Already retried this parameter, throw the error
2291
+ if (this.options.isVerbose) {
2292
+ console.warn(colors__default["default"].bgRed('Error'), `Parameter '${unsupportedParameter}' for model '${modelName}' already retried once, throwing error:`, error.message);
2293
+ }
2294
+ throw error;
2295
+ }
2296
+ // Mark this parameter as retried
2297
+ this.retriedUnsupportedParameters.add(retryKey);
2298
+ // Log warning in verbose mode
2299
+ if (this.options.isVerbose) {
2300
+ console.warn(colors__default["default"].bgYellow('Warning'), `Removing unsupported parameter '${unsupportedParameter}' for model '${modelName}' and retrying request`);
2301
+ }
2302
+ // Remove the unsupported parameter and retry
2303
+ const modifiedModelRequirements = removeUnsupportedModelRequirement(currentModelRequirements, unsupportedParameter);
2304
+ return this.callCompletionModelWithRetry(prompt, modifiedModelRequirements);
2143
2305
  }
2144
- const resultContent = rawResponse.choices[0].text;
2145
- const usage = this.computeUsage(content || '', resultContent || '', rawResponse);
2146
- return exportJson({
2147
- name: 'promptResult',
2148
- message: `Result of \`OpenAiCompatibleExecutionTools.callCompletionModel\``,
2149
- order: [],
2150
- value: {
2151
- content: resultContent,
2152
- modelName: rawResponse.model || modelName,
2153
- timing: {
2154
- start,
2155
- complete,
2156
- },
2157
- usage,
2158
- rawPromptContent,
2159
- rawRequest,
2160
- rawResponse,
2161
- // <- [🗯]
2162
- },
2163
- });
2164
2306
  }
2165
2307
  /**
2166
2308
  * Calls OpenAI compatible API to use a embedding model
@@ -2186,7 +2328,7 @@
2186
2328
  console.info(colors__default["default"].bgWhite('rawRequest'), JSON.stringify(rawRequest, null, 4));
2187
2329
  }
2188
2330
  const rawResponse = await this.limiter
2189
- .schedule(() => this.makeRequestWithRetry(() => client.embeddings.create(rawRequest)))
2331
+ .schedule(() => this.makeRequestWithNetworkRetry(() => client.embeddings.create(rawRequest)))
2190
2332
  .catch((error) => {
2191
2333
  assertsError(error);
2192
2334
  if (this.options.isVerbose) {
@@ -2248,7 +2390,7 @@
2248
2390
  /**
2249
2391
  * Makes a request with retry logic for network errors like ECONNRESET
2250
2392
  */
2251
- async makeRequestWithRetry(requestFn) {
2393
+ async makeRequestWithNetworkRetry(requestFn) {
2252
2394
  let lastError;
2253
2395
  for (let attempt = 1; attempt <= CONNECTION_RETRIES_LIMIT; attempt++) {
2254
2396
  try {
@@ -2260,8 +2402,8 @@
2260
2402
  // Check if this is a retryable network error
2261
2403
  const isRetryableError = this.isRetryableNetworkError(error);
2262
2404
  if (!isRetryableError || attempt === CONNECTION_RETRIES_LIMIT) {
2263
- if (this.options.isVerbose) {
2264
- console.info(colors__default["default"].bgRed('Final error after retries'), `Attempt ${attempt}/${CONNECTION_RETRIES_LIMIT}:`, error);
2405
+ if (this.options.isVerbose && this.isRetryableNetworkError(error)) {
2406
+ console.info(colors__default["default"].bgRed('Final network error after retries'), `Attempt ${attempt}/${CONNECTION_RETRIES_LIMIT}:`, error);
2265
2407
  }
2266
2408
  throw error;
2267
2409
  }
@@ -2271,7 +2413,7 @@
2271
2413
  const jitterDelay = Math.random() * 500; // Add some randomness
2272
2414
  const totalDelay = backoffDelay + jitterDelay;
2273
2415
  if (this.options.isVerbose) {
2274
- console.info(colors__default["default"].bgYellow('Retrying request'), `Attempt ${attempt}/${CONNECTION_RETRIES_LIMIT}, waiting ${Math.round(totalDelay)}ms:`, error.message);
2416
+ console.info(colors__default["default"].bgYellow('Retrying network request'), `Attempt ${attempt}/${CONNECTION_RETRIES_LIMIT}, waiting ${Math.round(totalDelay)}ms:`, error.message);
2275
2417
  }
2276
2418
  // Wait before retrying
2277
2419
  await new Promise((resolve) => setTimeout(resolve, totalDelay));