modelfusion 0.23.0 → 0.24.1

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/README.md CHANGED
@@ -35,6 +35,8 @@ You need to install `zod` and a matching version of `zod-to-json-schema` (peer d
35
35
  npm install zod zod-to-json-schema
36
36
  ```
37
37
 
38
+ Or use a template: [ModelFusion terminal app starter](https://github.com/lgrammel/modelfusion-terminal-app-starter)
39
+
38
40
  ## Usage Examples
39
41
 
40
42
  You can provide API keys for the different [integrations](https://modelfusion.dev/integration/model-provider/) using environment variables (e.g., `OPENAI_API_KEY`) or pass them into the model constructors as options.
@@ -4,7 +4,7 @@ import { Run } from "../../core/Run.js";
4
4
  import { RetryFunction } from "../../util/api/RetryFunction.js";
5
5
  import { ThrottleFunction } from "../../util/api/ThrottleFunction.js";
6
6
  import { CohereTextGenerationModelType } from "./CohereTextGenerationModel.js";
7
- import { CohereTextEmbeddingModelType } from "./index.js";
7
+ import { CohereTextEmbeddingModelType } from "./CohereTextEmbeddingModel.js";
8
8
  export type CohereTokenizerModelType = CohereTextGenerationModelType | CohereTextEmbeddingModelType;
9
9
  export interface CohereTokenizerSettings {
10
10
  model: CohereTokenizerModelType;
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.OpenAITextResponseFormat = exports.OpenAITextGenerationModel = exports.calculateOpenAITextGenerationCostInMillicents = exports.isOpenAITextGenerationModel = exports.OPENAI_TEXT_GENERATION_MODELS = void 0;
6
+ exports.OpenAITextResponseFormat = exports.OpenAITextGenerationModel = exports.calculateOpenAITextGenerationCostInMillicents = exports.isOpenAITextGenerationModel = exports.getOpenAITextGenerationModelInformation = exports.OPENAI_TEXT_GENERATION_MODELS = void 0;
7
7
  const secure_json_parse_1 = __importDefault(require("secure-json-parse"));
8
8
  const zod_1 = __importDefault(require("zod"));
9
9
  const AbstractModel_js_1 = require("../../model-function/AbstractModel.cjs");
@@ -23,10 +23,12 @@ exports.OPENAI_TEXT_GENERATION_MODELS = {
23
23
  "davinci-002": {
24
24
  contextWindowSize: 16384,
25
25
  tokenCostInMillicents: 0.2,
26
+ fineTunedTokenCostInMillicents: 1.2,
26
27
  },
27
28
  "babbage-002": {
28
29
  contextWindowSize: 16384,
29
30
  tokenCostInMillicents: 0.04,
31
+ fineTunedTokenCostInMillicents: 0.16,
30
32
  },
31
33
  "text-davinci-003": {
32
34
  contextWindowSize: 4096,
@@ -69,10 +71,38 @@ exports.OPENAI_TEXT_GENERATION_MODELS = {
69
71
  tokenCostInMillicents: 0.04,
70
72
  },
71
73
  };
72
- const isOpenAITextGenerationModel = (model) => model in exports.OPENAI_TEXT_GENERATION_MODELS;
74
+ function getOpenAITextGenerationModelInformation(model) {
75
+ // Model is already a base model:
76
+ if (model in exports.OPENAI_TEXT_GENERATION_MODELS) {
77
+ const baseModelInformation = exports.OPENAI_TEXT_GENERATION_MODELS[model];
78
+ return {
79
+ baseModel: model,
80
+ isFineTuned: false,
81
+ contextWindowSize: baseModelInformation.contextWindowSize,
82
+ tokenCostInMillicents: baseModelInformation.tokenCostInMillicents,
83
+ };
84
+ }
85
+ // Extract the base model from the fine-tuned model:
86
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
87
+ const [_, baseModel, ___, ____, _____] = model.split(":");
88
+ if (["davinci-002", "babbage-002"].includes(baseModel)) {
89
+ const baseModelInformation = exports.OPENAI_TEXT_GENERATION_MODELS[baseModel];
90
+ return {
91
+ baseModel: baseModel,
92
+ isFineTuned: true,
93
+ contextWindowSize: baseModelInformation.contextWindowSize,
94
+ tokenCostInMillicents: baseModelInformation.fineTunedTokenCostInMillicents,
95
+ };
96
+ }
97
+ throw new Error(`Unknown OpenAI chat base model ${baseModel}.`);
98
+ }
99
+ exports.getOpenAITextGenerationModelInformation = getOpenAITextGenerationModelInformation;
100
+ const isOpenAITextGenerationModel = (model) => model in exports.OPENAI_TEXT_GENERATION_MODELS ||
101
+ model.startsWith("ft:davinci-002:") ||
102
+ model.startsWith("ft:babbage-002:");
73
103
  exports.isOpenAITextGenerationModel = isOpenAITextGenerationModel;
74
104
  const calculateOpenAITextGenerationCostInMillicents = ({ model, response, }) => response.usage.total_tokens *
75
- exports.OPENAI_TEXT_GENERATION_MODELS[model].tokenCostInMillicents;
105
+ getOpenAITextGenerationModelInformation(model).tokenCostInMillicents;
76
106
  exports.calculateOpenAITextGenerationCostInMillicents = calculateOpenAITextGenerationCostInMillicents;
77
107
  /**
78
108
  * Create a text generation model that calls the OpenAI text completion API.
@@ -113,9 +143,11 @@ class OpenAITextGenerationModel extends AbstractModel_js_1.AbstractModel {
113
143
  writable: true,
114
144
  value: void 0
115
145
  });
116
- this.tokenizer = new TikTokenTokenizer_js_1.TikTokenTokenizer({ model: settings.model });
117
- this.contextWindowSize =
118
- exports.OPENAI_TEXT_GENERATION_MODELS[settings.model].contextWindowSize;
146
+ const modelInformation = getOpenAITextGenerationModelInformation(this.settings.model);
147
+ this.tokenizer = new TikTokenTokenizer_js_1.TikTokenTokenizer({
148
+ model: modelInformation.baseModel,
149
+ });
150
+ this.contextWindowSize = modelInformation.contextWindowSize;
119
151
  }
120
152
  get modelName() {
121
153
  return this.settings.model;
@@ -169,6 +201,7 @@ class OpenAITextGenerationModel extends AbstractModel_js_1.AbstractModel {
169
201
  "presencePenalty",
170
202
  "frequencyPenalty",
171
203
  "bestOf",
204
+ "logitBias",
172
205
  ];
173
206
  return Object.fromEntries(Object.entries(this.settings).filter(([key]) => eventSettingProperties.includes(key)));
174
207
  }
@@ -243,7 +276,7 @@ const openAITextGenerationResponseSchema = zod_1.default.object({
243
276
  *
244
277
  * console.log(response.choices[0].text);
245
278
  */
246
- async function callOpenAITextGenerationAPI({ baseUrl = "https://api.openai.com/v1", headers, abortSignal, responseFormat, apiKey, model, prompt, suffix, maxTokens, temperature, topP, n, logprobs, echo, stop, presencePenalty, frequencyPenalty, bestOf, user, }) {
279
+ async function callOpenAITextGenerationAPI({ baseUrl = "https://api.openai.com/v1", headers, abortSignal, responseFormat, apiKey, model, prompt, suffix, maxTokens, temperature, topP, n, logprobs, echo, stop, presencePenalty, frequencyPenalty, bestOf, logitBias, user, }) {
247
280
  return (0, postToApi_js_1.postJsonToApi)({
248
281
  url: `${baseUrl}/completions`,
249
282
  headers: {
@@ -265,6 +298,7 @@ async function callOpenAITextGenerationAPI({ baseUrl = "https://api.openai.com/v
265
298
  presence_penalty: presencePenalty,
266
299
  frequency_penalty: frequencyPenalty,
267
300
  best_of: bestOf,
301
+ logit_bias: logitBias,
268
302
  user,
269
303
  },
270
304
  failedResponseHandler: OpenAIError_js_1.failedOpenAICallResponseHandler,
@@ -5,8 +5,6 @@ import { DeltaEvent } from "../../model-function/generate-text/DeltaEvent.js";
5
5
  import { TextGenerationModel, TextGenerationModelSettings } from "../../model-function/generate-text/TextGenerationModel.js";
6
6
  import { PromptFormat } from "../../prompt/PromptFormat.js";
7
7
  import { PromptFormatTextGenerationModel } from "../../prompt/PromptFormatTextGenerationModel.js";
8
- import { RetryFunction } from "../../util/api/RetryFunction.js";
9
- import { ThrottleFunction } from "../../util/api/ThrottleFunction.js";
10
8
  import { ResponseHandler } from "../../util/api/postToApi.js";
11
9
  import { OpenAIImageGenerationCallSettings } from "./OpenAIImageGenerationModel.js";
12
10
  import { OpenAIModelSettings } from "./OpenAIModelSettings.js";
@@ -19,10 +17,12 @@ export declare const OPENAI_TEXT_GENERATION_MODELS: {
19
17
  "davinci-002": {
20
18
  contextWindowSize: number;
21
19
  tokenCostInMillicents: number;
20
+ fineTunedTokenCostInMillicents: number;
22
21
  };
23
22
  "babbage-002": {
24
23
  contextWindowSize: number;
25
24
  tokenCostInMillicents: number;
25
+ fineTunedTokenCostInMillicents: number;
26
26
  };
27
27
  "text-davinci-003": {
28
28
  contextWindowSize: number;
@@ -65,29 +65,39 @@ export declare const OPENAI_TEXT_GENERATION_MODELS: {
65
65
  tokenCostInMillicents: number;
66
66
  };
67
67
  };
68
- export type OpenAITextGenerationModelType = keyof typeof OPENAI_TEXT_GENERATION_MODELS;
69
- export declare const isOpenAITextGenerationModel: (model: string) => model is "davinci-002" | "babbage-002" | "text-davinci-003" | "text-davinci-002" | "code-davinci-002" | "davinci" | "text-curie-001" | "curie" | "text-babbage-001" | "babbage" | "text-ada-001" | "ada";
68
+ export declare function getOpenAITextGenerationModelInformation(model: OpenAITextGenerationModelType): {
69
+ baseModel: OpenAITextGenerationBaseModelType;
70
+ isFineTuned: boolean;
71
+ contextWindowSize: number;
72
+ tokenCostInMillicents: number;
73
+ };
74
+ type FineTuneableOpenAITextGenerationModelType = "davinci-002" | "babbage-002";
75
+ type FineTunedOpenAITextGenerationModelType = `ft:${FineTuneableOpenAITextGenerationModelType}:${string}:${string}:${string}`;
76
+ export type OpenAITextGenerationBaseModelType = keyof typeof OPENAI_TEXT_GENERATION_MODELS;
77
+ export type OpenAITextGenerationModelType = OpenAITextGenerationBaseModelType | FineTunedOpenAITextGenerationModelType;
78
+ export declare const isOpenAITextGenerationModel: (model: string) => model is OpenAITextGenerationModelType;
70
79
  export declare const calculateOpenAITextGenerationCostInMillicents: ({ model, response, }: {
71
80
  model: OpenAITextGenerationModelType;
72
81
  response: OpenAITextGenerationResponse;
73
82
  }) => number;
74
- export interface OpenAITextGenerationModelSettings extends TextGenerationModelSettings {
83
+ export interface OpenAITextGenerationCallSettings {
75
84
  model: OpenAITextGenerationModelType;
76
85
  headers?: Record<string, string>;
77
- baseUrl?: string;
78
- apiKey?: string;
79
- retry?: RetryFunction;
80
- throttle?: ThrottleFunction;
81
- isUserIdForwardingEnabled?: boolean;
82
86
  suffix?: string;
87
+ maxTokens?: number;
83
88
  temperature?: number;
84
89
  topP?: number;
85
90
  n?: number;
86
91
  logprobs?: number;
87
92
  echo?: boolean;
93
+ stop?: string | string[];
88
94
  presencePenalty?: number;
89
95
  frequencyPenalty?: number;
90
96
  bestOf?: number;
97
+ logitBias?: Record<number, number>;
98
+ }
99
+ export interface OpenAITextGenerationModelSettings extends TextGenerationModelSettings, OpenAIModelSettings, Omit<OpenAITextGenerationCallSettings, "stop" | "maxTokens"> {
100
+ isUserIdForwardingEnabled?: boolean;
91
101
  }
92
102
  /**
93
103
  * Create a text generation model that calls the OpenAI text completion API.
@@ -110,7 +120,7 @@ export interface OpenAITextGenerationModelSettings extends TextGenerationModelSe
110
120
  export declare class OpenAITextGenerationModel extends AbstractModel<OpenAITextGenerationModelSettings> implements TextGenerationModel<string, OpenAITextGenerationResponse, OpenAITextGenerationDelta, OpenAITextGenerationModelSettings> {
111
121
  constructor(settings: OpenAITextGenerationModelSettings);
112
122
  readonly provider: "openai";
113
- get modelName(): "davinci-002" | "babbage-002" | "text-davinci-003" | "text-davinci-002" | "code-davinci-002" | "davinci" | "text-curie-001" | "curie" | "text-babbage-001" | "babbage" | "text-ada-001" | "ada";
123
+ get modelName(): OpenAITextGenerationModelType;
114
124
  readonly contextWindowSize: number;
115
125
  readonly tokenizer: TikTokenTokenizer;
116
126
  private get apiKey();
@@ -126,8 +136,8 @@ export declare class OpenAITextGenerationModel extends AbstractModel<OpenAITextG
126
136
  model: string;
127
137
  usage: {
128
138
  prompt_tokens: number;
129
- total_tokens: number;
130
139
  completion_tokens: number;
140
+ total_tokens: number;
131
141
  };
132
142
  id: string;
133
143
  created: number;
@@ -176,20 +186,20 @@ declare const openAITextGenerationResponseSchema: z.ZodObject<{
176
186
  total_tokens: z.ZodNumber;
177
187
  }, "strip", z.ZodTypeAny, {
178
188
  prompt_tokens: number;
179
- total_tokens: number;
180
189
  completion_tokens: number;
190
+ total_tokens: number;
181
191
  }, {
182
192
  prompt_tokens: number;
183
- total_tokens: number;
184
193
  completion_tokens: number;
194
+ total_tokens: number;
185
195
  }>;
186
196
  }, "strip", z.ZodTypeAny, {
187
197
  object: "text_completion";
188
198
  model: string;
189
199
  usage: {
190
200
  prompt_tokens: number;
191
- total_tokens: number;
192
201
  completion_tokens: number;
202
+ total_tokens: number;
193
203
  };
194
204
  id: string;
195
205
  created: number;
@@ -204,8 +214,8 @@ declare const openAITextGenerationResponseSchema: z.ZodObject<{
204
214
  model: string;
205
215
  usage: {
206
216
  prompt_tokens: number;
207
- total_tokens: number;
208
217
  completion_tokens: number;
218
+ total_tokens: number;
209
219
  };
210
220
  id: string;
211
221
  created: number;
@@ -232,8 +242,8 @@ export declare const OpenAITextResponseFormat: {
232
242
  model: string;
233
243
  usage: {
234
244
  prompt_tokens: number;
235
- total_tokens: number;
236
245
  completion_tokens: number;
246
+ total_tokens: number;
237
247
  };
238
248
  id: string;
239
249
  created: number;
@@ -17,10 +17,12 @@ export const OPENAI_TEXT_GENERATION_MODELS = {
17
17
  "davinci-002": {
18
18
  contextWindowSize: 16384,
19
19
  tokenCostInMillicents: 0.2,
20
+ fineTunedTokenCostInMillicents: 1.2,
20
21
  },
21
22
  "babbage-002": {
22
23
  contextWindowSize: 16384,
23
24
  tokenCostInMillicents: 0.04,
25
+ fineTunedTokenCostInMillicents: 0.16,
24
26
  },
25
27
  "text-davinci-003": {
26
28
  contextWindowSize: 4096,
@@ -63,9 +65,36 @@ export const OPENAI_TEXT_GENERATION_MODELS = {
63
65
  tokenCostInMillicents: 0.04,
64
66
  },
65
67
  };
66
- export const isOpenAITextGenerationModel = (model) => model in OPENAI_TEXT_GENERATION_MODELS;
68
+ export function getOpenAITextGenerationModelInformation(model) {
69
+ // Model is already a base model:
70
+ if (model in OPENAI_TEXT_GENERATION_MODELS) {
71
+ const baseModelInformation = OPENAI_TEXT_GENERATION_MODELS[model];
72
+ return {
73
+ baseModel: model,
74
+ isFineTuned: false,
75
+ contextWindowSize: baseModelInformation.contextWindowSize,
76
+ tokenCostInMillicents: baseModelInformation.tokenCostInMillicents,
77
+ };
78
+ }
79
+ // Extract the base model from the fine-tuned model:
80
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
81
+ const [_, baseModel, ___, ____, _____] = model.split(":");
82
+ if (["davinci-002", "babbage-002"].includes(baseModel)) {
83
+ const baseModelInformation = OPENAI_TEXT_GENERATION_MODELS[baseModel];
84
+ return {
85
+ baseModel: baseModel,
86
+ isFineTuned: true,
87
+ contextWindowSize: baseModelInformation.contextWindowSize,
88
+ tokenCostInMillicents: baseModelInformation.fineTunedTokenCostInMillicents,
89
+ };
90
+ }
91
+ throw new Error(`Unknown OpenAI chat base model ${baseModel}.`);
92
+ }
93
+ export const isOpenAITextGenerationModel = (model) => model in OPENAI_TEXT_GENERATION_MODELS ||
94
+ model.startsWith("ft:davinci-002:") ||
95
+ model.startsWith("ft:babbage-002:");
67
96
  export const calculateOpenAITextGenerationCostInMillicents = ({ model, response, }) => response.usage.total_tokens *
68
- OPENAI_TEXT_GENERATION_MODELS[model].tokenCostInMillicents;
97
+ getOpenAITextGenerationModelInformation(model).tokenCostInMillicents;
69
98
  /**
70
99
  * Create a text generation model that calls the OpenAI text completion API.
71
100
  *
@@ -105,9 +134,11 @@ export class OpenAITextGenerationModel extends AbstractModel {
105
134
  writable: true,
106
135
  value: void 0
107
136
  });
108
- this.tokenizer = new TikTokenTokenizer({ model: settings.model });
109
- this.contextWindowSize =
110
- OPENAI_TEXT_GENERATION_MODELS[settings.model].contextWindowSize;
137
+ const modelInformation = getOpenAITextGenerationModelInformation(this.settings.model);
138
+ this.tokenizer = new TikTokenTokenizer({
139
+ model: modelInformation.baseModel,
140
+ });
141
+ this.contextWindowSize = modelInformation.contextWindowSize;
111
142
  }
112
143
  get modelName() {
113
144
  return this.settings.model;
@@ -161,6 +192,7 @@ export class OpenAITextGenerationModel extends AbstractModel {
161
192
  "presencePenalty",
162
193
  "frequencyPenalty",
163
194
  "bestOf",
195
+ "logitBias",
164
196
  ];
165
197
  return Object.fromEntries(Object.entries(this.settings).filter(([key]) => eventSettingProperties.includes(key)));
166
198
  }
@@ -234,7 +266,7 @@ const openAITextGenerationResponseSchema = z.object({
234
266
  *
235
267
  * console.log(response.choices[0].text);
236
268
  */
237
- async function callOpenAITextGenerationAPI({ baseUrl = "https://api.openai.com/v1", headers, abortSignal, responseFormat, apiKey, model, prompt, suffix, maxTokens, temperature, topP, n, logprobs, echo, stop, presencePenalty, frequencyPenalty, bestOf, user, }) {
269
+ async function callOpenAITextGenerationAPI({ baseUrl = "https://api.openai.com/v1", headers, abortSignal, responseFormat, apiKey, model, prompt, suffix, maxTokens, temperature, topP, n, logprobs, echo, stop, presencePenalty, frequencyPenalty, bestOf, logitBias, user, }) {
238
270
  return postJsonToApi({
239
271
  url: `${baseUrl}/completions`,
240
272
  headers: {
@@ -256,6 +288,7 @@ async function callOpenAITextGenerationAPI({ baseUrl = "https://api.openai.com/v
256
288
  presence_penalty: presencePenalty,
257
289
  frequency_penalty: frequencyPenalty,
258
290
  best_of: bestOf,
291
+ logit_bias: logitBias,
259
292
  user,
260
293
  },
261
294
  failedResponseHandler: failedOpenAICallResponseHandler,
@@ -1,6 +1,8 @@
1
1
  import { TiktokenEncoding } from "js-tiktoken";
2
2
  import { FullTokenizer } from "../../model-function/tokenize-text/Tokenizer.js";
3
- import { OpenAIChatModelType, OpenAITextEmbeddingModelType, OpenAITextGenerationModelType } from "./index.js";
3
+ import { OpenAITextEmbeddingModelType } from "./OpenAITextEmbeddingModel.js";
4
+ import { OpenAITextGenerationBaseModelType } from "./OpenAITextGenerationModel.js";
5
+ import { OpenAIChatBaseModelType } from "./chat/OpenAIChatModel.js";
4
6
  /**
5
7
  * TikToken tokenizer for OpenAI language models.
6
8
  *
@@ -21,7 +23,7 @@ export declare class TikTokenTokenizer implements FullTokenizer {
21
23
  * Get a TikToken tokenizer for a specific model or encoding.
22
24
  */
23
25
  constructor(options: {
24
- model: OpenAIChatModelType | OpenAITextEmbeddingModelType | OpenAITextGenerationModelType;
26
+ model: OpenAIChatBaseModelType | OpenAITextGenerationBaseModelType | OpenAITextEmbeddingModelType;
25
27
  } | {
26
28
  encoding: TiktokenEncoding;
27
29
  });
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.OpenAIChatResponseFormat = exports.OpenAIChatModel = exports.calculateOpenAIChatCostInMillicents = exports.isOpenAIChatModel = exports.OPENAI_CHAT_MODELS = void 0;
6
+ exports.OpenAIChatResponseFormat = exports.OpenAIChatModel = exports.calculateOpenAIChatCostInMillicents = exports.isOpenAIChatModel = exports.getOpenAIChatModelInformation = exports.OPENAI_CHAT_MODELS = void 0;
7
7
  const secure_json_parse_1 = __importDefault(require("secure-json-parse"));
8
8
  const zod_1 = __importDefault(require("zod"));
9
9
  const AbstractModel_js_1 = require("../../../model-function/AbstractModel.cjs");
@@ -55,6 +55,8 @@ exports.OPENAI_CHAT_MODELS = {
55
55
  contextWindowSize: 4096,
56
56
  promptTokenCostInMillicents: 0.15,
57
57
  completionTokenCostInMillicents: 0.2,
58
+ fineTunedPromptTokenCostInMillicents: 1.2,
59
+ fineTunedCompletionTokenCostInMillicents: 1.6,
58
60
  },
59
61
  "gpt-3.5-turbo-0301": {
60
62
  contextWindowSize: 4096,
@@ -65,6 +67,8 @@ exports.OPENAI_CHAT_MODELS = {
65
67
  contextWindowSize: 4096,
66
68
  promptTokenCostInMillicents: 0.15,
67
69
  completionTokenCostInMillicents: 0.2,
70
+ fineTunedPromptTokenCostInMillicents: 1.2,
71
+ fineTunedCompletionTokenCostInMillicents: 1.6,
68
72
  },
69
73
  "gpt-3.5-turbo-16k": {
70
74
  contextWindowSize: 16384,
@@ -77,12 +81,45 @@ exports.OPENAI_CHAT_MODELS = {
77
81
  completionTokenCostInMillicents: 0.4,
78
82
  },
79
83
  };
80
- const isOpenAIChatModel = (model) => model in exports.OPENAI_CHAT_MODELS;
84
+ function getOpenAIChatModelInformation(model) {
85
+ // Model is already a base model:
86
+ if (model in exports.OPENAI_CHAT_MODELS) {
87
+ const baseModelInformation = exports.OPENAI_CHAT_MODELS[model];
88
+ return {
89
+ baseModel: model,
90
+ isFineTuned: false,
91
+ contextWindowSize: baseModelInformation.contextWindowSize,
92
+ promptTokenCostInMillicents: baseModelInformation.promptTokenCostInMillicents,
93
+ completionTokenCostInMillicents: baseModelInformation.completionTokenCostInMillicents,
94
+ };
95
+ }
96
+ // Extract the base model from the fine-tuned model:
97
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
98
+ const [_, baseModel, ___, ____, _____] = model.split(":");
99
+ if (["gpt-3.5-turbo", "gpt-3.5-turbo-0613"].includes(baseModel)) {
100
+ const baseModelInformation = exports.OPENAI_CHAT_MODELS[baseModel];
101
+ return {
102
+ baseModel: baseModel,
103
+ isFineTuned: true,
104
+ contextWindowSize: baseModelInformation.contextWindowSize,
105
+ promptTokenCostInMillicents: baseModelInformation.fineTunedPromptTokenCostInMillicents,
106
+ completionTokenCostInMillicents: baseModelInformation.fineTunedCompletionTokenCostInMillicents,
107
+ };
108
+ }
109
+ throw new Error(`Unknown OpenAI chat base model ${baseModel}.`);
110
+ }
111
+ exports.getOpenAIChatModelInformation = getOpenAIChatModelInformation;
112
+ const isOpenAIChatModel = (model) => model in exports.OPENAI_CHAT_MODELS ||
113
+ model.startsWith("ft:gpt-3.5-turbo-0613:") ||
114
+ model.startsWith("ft:gpt-3.5-turbo:");
81
115
  exports.isOpenAIChatModel = isOpenAIChatModel;
82
- const calculateOpenAIChatCostInMillicents = ({ model, response, }) => response.usage.prompt_tokens *
83
- exports.OPENAI_CHAT_MODELS[model].promptTokenCostInMillicents +
84
- response.usage.completion_tokens *
85
- exports.OPENAI_CHAT_MODELS[model].completionTokenCostInMillicents;
116
+ const calculateOpenAIChatCostInMillicents = ({ model, response, }) => {
117
+ const modelInformation = getOpenAIChatModelInformation(model);
118
+ return (response.usage.prompt_tokens *
119
+ modelInformation.promptTokenCostInMillicents +
120
+ response.usage.completion_tokens *
121
+ modelInformation.completionTokenCostInMillicents);
122
+ };
86
123
  exports.calculateOpenAIChatCostInMillicents = calculateOpenAIChatCostInMillicents;
87
124
  /**
88
125
  * Create a text generation model that calls the OpenAI chat completion API.
@@ -124,9 +161,11 @@ class OpenAIChatModel extends AbstractModel_js_1.AbstractModel {
124
161
  writable: true,
125
162
  value: void 0
126
163
  });
127
- this.tokenizer = new TikTokenTokenizer_js_1.TikTokenTokenizer({ model: this.settings.model });
128
- this.contextWindowSize =
129
- exports.OPENAI_CHAT_MODELS[this.settings.model].contextWindowSize;
164
+ const modelInformation = getOpenAIChatModelInformation(this.settings.model);
165
+ this.tokenizer = new TikTokenTokenizer_js_1.TikTokenTokenizer({
166
+ model: modelInformation.baseModel,
167
+ });
168
+ this.contextWindowSize = modelInformation.contextWindowSize;
130
169
  }
131
170
  get modelName() {
132
171
  return this.settings.model;
@@ -180,6 +219,9 @@ class OpenAIChatModel extends AbstractModel_js_1.AbstractModel {
180
219
  "temperature",
181
220
  "topP",
182
221
  "n",
222
+ "presencePenalty",
223
+ "frequencyPenalty",
224
+ "logitBias",
183
225
  ];
184
226
  return Object.fromEntries(Object.entries(this.settings).filter(([key]) => eventSettingProperties.includes(key)));
185
227
  }
@@ -268,7 +310,7 @@ const openAIChatResponseSchema = zod_1.default.object({
268
310
  total_tokens: zod_1.default.number(),
269
311
  }),
270
312
  });
271
- async function callOpenAIChatCompletionAPI({ baseUrl = "https://api.openai.com/v1", headers, abortSignal, responseFormat, apiKey, model, messages, functions, functionCall, temperature, topP, n, stop, maxTokens, presencePenalty, frequencyPenalty, user, }) {
313
+ async function callOpenAIChatCompletionAPI({ baseUrl = "https://api.openai.com/v1", headers, abortSignal, responseFormat, apiKey, model, messages, functions, functionCall, temperature, topP, n, stop, maxTokens, presencePenalty, frequencyPenalty, logitBias, user, }) {
272
314
  return (0, postToApi_js_1.postJsonToApi)({
273
315
  url: `${baseUrl}/chat/completions`,
274
316
  headers: {
@@ -288,6 +330,7 @@ async function callOpenAIChatCompletionAPI({ baseUrl = "https://api.openai.com/v
288
330
  max_tokens: maxTokens,
289
331
  presence_penalty: presencePenalty,
290
332
  frequency_penalty: frequencyPenalty,
333
+ logit_bias: logitBias,
291
334
  user,
292
335
  },
293
336
  failedResponseHandler: OpenAIError_js_1.failedOpenAICallResponseHandler,
@@ -48,6 +48,8 @@ export declare const OPENAI_CHAT_MODELS: {
48
48
  contextWindowSize: number;
49
49
  promptTokenCostInMillicents: number;
50
50
  completionTokenCostInMillicents: number;
51
+ fineTunedPromptTokenCostInMillicents: number;
52
+ fineTunedCompletionTokenCostInMillicents: number;
51
53
  };
52
54
  "gpt-3.5-turbo-0301": {
53
55
  contextWindowSize: number;
@@ -58,6 +60,8 @@ export declare const OPENAI_CHAT_MODELS: {
58
60
  contextWindowSize: number;
59
61
  promptTokenCostInMillicents: number;
60
62
  completionTokenCostInMillicents: number;
63
+ fineTunedPromptTokenCostInMillicents: number;
64
+ fineTunedCompletionTokenCostInMillicents: number;
61
65
  };
62
66
  "gpt-3.5-turbo-16k": {
63
67
  contextWindowSize: number;
@@ -70,8 +74,18 @@ export declare const OPENAI_CHAT_MODELS: {
70
74
  completionTokenCostInMillicents: number;
71
75
  };
72
76
  };
73
- export type OpenAIChatModelType = keyof typeof OPENAI_CHAT_MODELS;
74
- export declare const isOpenAIChatModel: (model: string) => model is "gpt-4" | "gpt-4-0314" | "gpt-4-0613" | "gpt-4-32k" | "gpt-4-32k-0314" | "gpt-4-32k-0613" | "gpt-3.5-turbo" | "gpt-3.5-turbo-0301" | "gpt-3.5-turbo-0613" | "gpt-3.5-turbo-16k" | "gpt-3.5-turbo-16k-0613";
77
+ export declare function getOpenAIChatModelInformation(model: OpenAIChatModelType): {
78
+ baseModel: OpenAIChatBaseModelType;
79
+ isFineTuned: boolean;
80
+ contextWindowSize: number;
81
+ promptTokenCostInMillicents: number;
82
+ completionTokenCostInMillicents: number;
83
+ };
84
+ type FineTuneableOpenAIChatModelType = `gpt-3.5-turbo` | `gpt-3.5-turbo-0613`;
85
+ type FineTunedOpenAIChatModelType = `ft:${FineTuneableOpenAIChatModelType}:${string}:${string}:${string}`;
86
+ export type OpenAIChatBaseModelType = keyof typeof OPENAI_CHAT_MODELS;
87
+ export type OpenAIChatModelType = OpenAIChatBaseModelType | FineTunedOpenAIChatModelType;
88
+ export declare const isOpenAIChatModel: (model: string) => model is OpenAIChatModelType;
75
89
  export declare const calculateOpenAIChatCostInMillicents: ({ model, response, }: {
76
90
  model: OpenAIChatModelType;
77
91
  response: OpenAIChatResponse;
@@ -87,13 +101,14 @@ export interface OpenAIChatCallSettings {
87
101
  functionCall?: "none" | "auto" | {
88
102
  name: string;
89
103
  };
104
+ stop?: string | string[];
105
+ maxTokens?: number;
90
106
  temperature?: number;
91
107
  topP?: number;
92
108
  n?: number;
93
- stop?: string | string[];
94
- maxTokens?: number;
95
109
  presencePenalty?: number;
96
110
  frequencyPenalty?: number;
111
+ logitBias?: Record<number, number>;
97
112
  }
98
113
  export interface OpenAIChatSettings extends TextGenerationModelSettings, OpenAIModelSettings, Omit<OpenAIChatCallSettings, "stop" | "maxTokens"> {
99
114
  isUserIdForwardingEnabled?: boolean;
@@ -120,7 +135,7 @@ export interface OpenAIChatSettings extends TextGenerationModelSettings, OpenAIM
120
135
  export declare class OpenAIChatModel extends AbstractModel<OpenAIChatSettings> implements TextGenerationModel<OpenAIChatMessage[], OpenAIChatResponse, OpenAIChatDelta, OpenAIChatSettings>, JsonGenerationModel<OpenAIChatSingleFunctionPrompt<unknown>, OpenAIChatResponse, OpenAIChatSettings>, JsonOrTextGenerationModel<OpenAIChatAutoFunctionPrompt<Array<OpenAIFunctionDescription<unknown>>>, OpenAIChatResponse, OpenAIChatSettings> {
121
136
  constructor(settings: OpenAIChatSettings);
122
137
  readonly provider: "openai";
123
- get modelName(): "gpt-4" | "gpt-4-0314" | "gpt-4-0613" | "gpt-4-32k" | "gpt-4-32k-0314" | "gpt-4-32k-0613" | "gpt-3.5-turbo" | "gpt-3.5-turbo-0301" | "gpt-3.5-turbo-0613" | "gpt-3.5-turbo-16k" | "gpt-3.5-turbo-16k-0613";
138
+ get modelName(): OpenAIChatModelType;
124
139
  readonly contextWindowSize: number;
125
140
  readonly tokenizer: TikTokenTokenizer;
126
141
  private get apiKey();
@@ -140,8 +155,8 @@ export declare class OpenAIChatModel extends AbstractModel<OpenAIChatSettings> i
140
155
  model: string;
141
156
  usage: {
142
157
  prompt_tokens: number;
143
- total_tokens: number;
144
158
  completion_tokens: number;
159
+ total_tokens: number;
145
160
  };
146
161
  id: string;
147
162
  created: number;
@@ -247,20 +262,20 @@ declare const openAIChatResponseSchema: z.ZodObject<{
247
262
  total_tokens: z.ZodNumber;
248
263
  }, "strip", z.ZodTypeAny, {
249
264
  prompt_tokens: number;
250
- total_tokens: number;
251
265
  completion_tokens: number;
266
+ total_tokens: number;
252
267
  }, {
253
268
  prompt_tokens: number;
254
- total_tokens: number;
255
269
  completion_tokens: number;
270
+ total_tokens: number;
256
271
  }>;
257
272
  }, "strip", z.ZodTypeAny, {
258
273
  object: "chat.completion";
259
274
  model: string;
260
275
  usage: {
261
276
  prompt_tokens: number;
262
- total_tokens: number;
263
277
  completion_tokens: number;
278
+ total_tokens: number;
264
279
  };
265
280
  id: string;
266
281
  created: number;
@@ -282,8 +297,8 @@ declare const openAIChatResponseSchema: z.ZodObject<{
282
297
  model: string;
283
298
  usage: {
284
299
  prompt_tokens: number;
285
- total_tokens: number;
286
300
  completion_tokens: number;
301
+ total_tokens: number;
287
302
  };
288
303
  id: string;
289
304
  created: number;
@@ -317,8 +332,8 @@ export declare const OpenAIChatResponseFormat: {
317
332
  model: string;
318
333
  usage: {
319
334
  prompt_tokens: number;
320
- total_tokens: number;
321
335
  completion_tokens: number;
336
+ total_tokens: number;
322
337
  };
323
338
  id: string;
324
339
  created: number;
@@ -49,6 +49,8 @@ export const OPENAI_CHAT_MODELS = {
49
49
  contextWindowSize: 4096,
50
50
  promptTokenCostInMillicents: 0.15,
51
51
  completionTokenCostInMillicents: 0.2,
52
+ fineTunedPromptTokenCostInMillicents: 1.2,
53
+ fineTunedCompletionTokenCostInMillicents: 1.6,
52
54
  },
53
55
  "gpt-3.5-turbo-0301": {
54
56
  contextWindowSize: 4096,
@@ -59,6 +61,8 @@ export const OPENAI_CHAT_MODELS = {
59
61
  contextWindowSize: 4096,
60
62
  promptTokenCostInMillicents: 0.15,
61
63
  completionTokenCostInMillicents: 0.2,
64
+ fineTunedPromptTokenCostInMillicents: 1.2,
65
+ fineTunedCompletionTokenCostInMillicents: 1.6,
62
66
  },
63
67
  "gpt-3.5-turbo-16k": {
64
68
  contextWindowSize: 16384,
@@ -71,11 +75,43 @@ export const OPENAI_CHAT_MODELS = {
71
75
  completionTokenCostInMillicents: 0.4,
72
76
  },
73
77
  };
74
- export const isOpenAIChatModel = (model) => model in OPENAI_CHAT_MODELS;
75
- export const calculateOpenAIChatCostInMillicents = ({ model, response, }) => response.usage.prompt_tokens *
76
- OPENAI_CHAT_MODELS[model].promptTokenCostInMillicents +
77
- response.usage.completion_tokens *
78
- OPENAI_CHAT_MODELS[model].completionTokenCostInMillicents;
78
+ export function getOpenAIChatModelInformation(model) {
79
+ // Model is already a base model:
80
+ if (model in OPENAI_CHAT_MODELS) {
81
+ const baseModelInformation = OPENAI_CHAT_MODELS[model];
82
+ return {
83
+ baseModel: model,
84
+ isFineTuned: false,
85
+ contextWindowSize: baseModelInformation.contextWindowSize,
86
+ promptTokenCostInMillicents: baseModelInformation.promptTokenCostInMillicents,
87
+ completionTokenCostInMillicents: baseModelInformation.completionTokenCostInMillicents,
88
+ };
89
+ }
90
+ // Extract the base model from the fine-tuned model:
91
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
92
+ const [_, baseModel, ___, ____, _____] = model.split(":");
93
+ if (["gpt-3.5-turbo", "gpt-3.5-turbo-0613"].includes(baseModel)) {
94
+ const baseModelInformation = OPENAI_CHAT_MODELS[baseModel];
95
+ return {
96
+ baseModel: baseModel,
97
+ isFineTuned: true,
98
+ contextWindowSize: baseModelInformation.contextWindowSize,
99
+ promptTokenCostInMillicents: baseModelInformation.fineTunedPromptTokenCostInMillicents,
100
+ completionTokenCostInMillicents: baseModelInformation.fineTunedCompletionTokenCostInMillicents,
101
+ };
102
+ }
103
+ throw new Error(`Unknown OpenAI chat base model ${baseModel}.`);
104
+ }
105
+ export const isOpenAIChatModel = (model) => model in OPENAI_CHAT_MODELS ||
106
+ model.startsWith("ft:gpt-3.5-turbo-0613:") ||
107
+ model.startsWith("ft:gpt-3.5-turbo:");
108
+ export const calculateOpenAIChatCostInMillicents = ({ model, response, }) => {
109
+ const modelInformation = getOpenAIChatModelInformation(model);
110
+ return (response.usage.prompt_tokens *
111
+ modelInformation.promptTokenCostInMillicents +
112
+ response.usage.completion_tokens *
113
+ modelInformation.completionTokenCostInMillicents);
114
+ };
79
115
  /**
80
116
  * Create a text generation model that calls the OpenAI chat completion API.
81
117
  *
@@ -116,9 +152,11 @@ export class OpenAIChatModel extends AbstractModel {
116
152
  writable: true,
117
153
  value: void 0
118
154
  });
119
- this.tokenizer = new TikTokenTokenizer({ model: this.settings.model });
120
- this.contextWindowSize =
121
- OPENAI_CHAT_MODELS[this.settings.model].contextWindowSize;
155
+ const modelInformation = getOpenAIChatModelInformation(this.settings.model);
156
+ this.tokenizer = new TikTokenTokenizer({
157
+ model: modelInformation.baseModel,
158
+ });
159
+ this.contextWindowSize = modelInformation.contextWindowSize;
122
160
  }
123
161
  get modelName() {
124
162
  return this.settings.model;
@@ -172,6 +210,9 @@ export class OpenAIChatModel extends AbstractModel {
172
210
  "temperature",
173
211
  "topP",
174
212
  "n",
213
+ "presencePenalty",
214
+ "frequencyPenalty",
215
+ "logitBias",
175
216
  ];
176
217
  return Object.fromEntries(Object.entries(this.settings).filter(([key]) => eventSettingProperties.includes(key)));
177
218
  }
@@ -259,7 +300,7 @@ const openAIChatResponseSchema = z.object({
259
300
  total_tokens: z.number(),
260
301
  }),
261
302
  });
262
- async function callOpenAIChatCompletionAPI({ baseUrl = "https://api.openai.com/v1", headers, abortSignal, responseFormat, apiKey, model, messages, functions, functionCall, temperature, topP, n, stop, maxTokens, presencePenalty, frequencyPenalty, user, }) {
303
+ async function callOpenAIChatCompletionAPI({ baseUrl = "https://api.openai.com/v1", headers, abortSignal, responseFormat, apiKey, model, messages, functions, functionCall, temperature, topP, n, stop, maxTokens, presencePenalty, frequencyPenalty, logitBias, user, }) {
263
304
  return postJsonToApi({
264
305
  url: `${baseUrl}/chat/completions`,
265
306
  headers: {
@@ -279,6 +320,7 @@ async function callOpenAIChatCompletionAPI({ baseUrl = "https://api.openai.com/v
279
320
  max_tokens: maxTokens,
280
321
  presence_penalty: presencePenalty,
281
322
  frequency_penalty: frequencyPenalty,
323
+ logit_bias: logitBias,
282
324
  user,
283
325
  },
284
326
  failedResponseHandler: failedOpenAICallResponseHandler,
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.countOpenAIChatPromptTokens = exports.countOpenAIChatMessageTokens = exports.OPENAI_CHAT_MESSAGE_BASE_TOKEN_COUNT = exports.OPENAI_CHAT_PROMPT_BASE_TOKEN_COUNT = void 0;
4
4
  const countTokens_js_1 = require("../../../model-function/tokenize-text/countTokens.cjs");
5
5
  const TikTokenTokenizer_js_1 = require("../TikTokenTokenizer.cjs");
6
+ const OpenAIChatModel_js_1 = require("./OpenAIChatModel.cjs");
6
7
  /**
7
8
  * Prompt tokens that are included automatically for every full
8
9
  * chat prompt (several messages) that is sent to OpenAI.
@@ -14,8 +15,10 @@ exports.OPENAI_CHAT_PROMPT_BASE_TOKEN_COUNT = 2;
14
15
  */
15
16
  exports.OPENAI_CHAT_MESSAGE_BASE_TOKEN_COUNT = 5;
16
17
  async function countOpenAIChatMessageTokens({ message, model, }) {
17
- return (exports.OPENAI_CHAT_MESSAGE_BASE_TOKEN_COUNT +
18
- (await (0, countTokens_js_1.countTokens)(new TikTokenTokenizer_js_1.TikTokenTokenizer({ model }), message.content ?? "")));
18
+ const contentTokenCount = await (0, countTokens_js_1.countTokens)(new TikTokenTokenizer_js_1.TikTokenTokenizer({
19
+ model: (0, OpenAIChatModel_js_1.getOpenAIChatModelInformation)(model).baseModel,
20
+ }), message.content ?? "");
21
+ return exports.OPENAI_CHAT_MESSAGE_BASE_TOKEN_COUNT + contentTokenCount;
19
22
  }
20
23
  exports.countOpenAIChatMessageTokens = countOpenAIChatMessageTokens;
21
24
  async function countOpenAIChatPromptTokens({ messages, model, }) {
@@ -1,5 +1,6 @@
1
1
  import { countTokens } from "../../../model-function/tokenize-text/countTokens.js";
2
2
  import { TikTokenTokenizer } from "../TikTokenTokenizer.js";
3
+ import { getOpenAIChatModelInformation, } from "./OpenAIChatModel.js";
3
4
  /**
4
5
  * Prompt tokens that are included automatically for every full
5
6
  * chat prompt (several messages) that is sent to OpenAI.
@@ -11,8 +12,10 @@ export const OPENAI_CHAT_PROMPT_BASE_TOKEN_COUNT = 2;
11
12
  */
12
13
  export const OPENAI_CHAT_MESSAGE_BASE_TOKEN_COUNT = 5;
13
14
  export async function countOpenAIChatMessageTokens({ message, model, }) {
14
- return (OPENAI_CHAT_MESSAGE_BASE_TOKEN_COUNT +
15
- (await countTokens(new TikTokenTokenizer({ model }), message.content ?? "")));
15
+ const contentTokenCount = await countTokens(new TikTokenTokenizer({
16
+ model: getOpenAIChatModelInformation(model).baseModel,
17
+ }), message.content ?? "");
18
+ return OPENAI_CHAT_MESSAGE_BASE_TOKEN_COUNT + contentTokenCount;
16
19
  }
17
20
  export async function countOpenAIChatPromptTokens({ messages, model, }) {
18
21
  let tokens = OPENAI_CHAT_PROMPT_BASE_TOKEN_COUNT;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "modelfusion",
3
3
  "description": "Build AI applications, chatbots, and agents with JavaScript and TypeScript.",
4
- "version": "0.23.0",
4
+ "version": "0.24.1",
5
5
  "author": "Lars Grammel",
6
6
  "license": "MIT",
7
7
  "keywords": [