@node-llm/core 0.4.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/README.md +89 -27
  2. package/dist/chat/Chat.d.ts +6 -1
  3. package/dist/chat/Chat.d.ts.map +1 -1
  4. package/dist/chat/Chat.js +27 -4
  5. package/dist/chat/ChatOptions.d.ts +3 -0
  6. package/dist/chat/ChatOptions.d.ts.map +1 -1
  7. package/dist/chat/ChatResponse.d.ts +3 -0
  8. package/dist/chat/ChatResponse.d.ts.map +1 -1
  9. package/dist/chat/ChatResponse.js +3 -0
  10. package/dist/index.d.ts +2 -0
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +2 -0
  13. package/dist/llm.d.ts +5 -1
  14. package/dist/llm.d.ts.map +1 -1
  15. package/dist/llm.js +22 -6
  16. package/dist/models/ModelRegistry.d.ts +39 -12
  17. package/dist/models/ModelRegistry.d.ts.map +1 -1
  18. package/dist/models/ModelRegistry.js +50 -40
  19. package/dist/models/models.d.ts +972 -0
  20. package/dist/models/models.d.ts.map +1 -0
  21. package/dist/models/models.js +7026 -0
  22. package/dist/models/types.d.ts +50 -0
  23. package/dist/models/types.d.ts.map +1 -0
  24. package/dist/models/types.js +1 -0
  25. package/dist/providers/Provider.d.ts +5 -0
  26. package/dist/providers/Provider.d.ts.map +1 -1
  27. package/dist/providers/anthropic/AnthropicProvider.d.ts +32 -0
  28. package/dist/providers/anthropic/AnthropicProvider.d.ts.map +1 -0
  29. package/dist/providers/anthropic/AnthropicProvider.js +49 -0
  30. package/dist/providers/anthropic/Capabilities.d.ts +11 -0
  31. package/dist/providers/anthropic/Capabilities.d.ts.map +1 -0
  32. package/dist/providers/anthropic/Capabilities.js +82 -0
  33. package/dist/providers/anthropic/Chat.d.ts +8 -0
  34. package/dist/providers/anthropic/Chat.d.ts.map +1 -0
  35. package/dist/providers/anthropic/Chat.js +97 -0
  36. package/dist/providers/anthropic/Errors.d.ts +2 -0
  37. package/dist/providers/anthropic/Errors.d.ts.map +1 -0
  38. package/dist/providers/anthropic/Errors.js +33 -0
  39. package/dist/providers/anthropic/Models.d.ts +9 -0
  40. package/dist/providers/anthropic/Models.d.ts.map +1 -0
  41. package/dist/providers/anthropic/Models.js +58 -0
  42. package/dist/providers/anthropic/Streaming.d.ts +8 -0
  43. package/dist/providers/anthropic/Streaming.d.ts.map +1 -0
  44. package/dist/providers/anthropic/Streaming.js +113 -0
  45. package/dist/providers/anthropic/Utils.d.ts +5 -0
  46. package/dist/providers/anthropic/Utils.d.ts.map +1 -0
  47. package/dist/providers/anthropic/Utils.js +125 -0
  48. package/dist/providers/anthropic/index.d.ts +2 -0
  49. package/dist/providers/anthropic/index.d.ts.map +1 -0
  50. package/dist/providers/anthropic/index.js +11 -0
  51. package/dist/providers/anthropic/types.d.ts +57 -0
  52. package/dist/providers/anthropic/types.d.ts.map +1 -0
  53. package/dist/providers/anthropic/types.js +1 -0
  54. package/dist/providers/gemini/Capabilities.d.ts +28 -7
  55. package/dist/providers/gemini/Capabilities.d.ts.map +1 -1
  56. package/dist/providers/gemini/Capabilities.js +37 -22
  57. package/dist/providers/gemini/Chat.d.ts +1 -0
  58. package/dist/providers/gemini/Chat.d.ts.map +1 -1
  59. package/dist/providers/gemini/Chat.js +40 -3
  60. package/dist/providers/gemini/GeminiProvider.d.ts +2 -1
  61. package/dist/providers/gemini/GeminiProvider.d.ts.map +1 -1
  62. package/dist/providers/gemini/GeminiProvider.js +3 -0
  63. package/dist/providers/gemini/Models.d.ts +1 -0
  64. package/dist/providers/gemini/Models.d.ts.map +1 -1
  65. package/dist/providers/gemini/Models.js +46 -26
  66. package/dist/providers/gemini/Streaming.d.ts +1 -0
  67. package/dist/providers/gemini/Streaming.d.ts.map +1 -1
  68. package/dist/providers/gemini/Streaming.js +34 -4
  69. package/dist/providers/openai/Capabilities.d.ts +3 -11
  70. package/dist/providers/openai/Capabilities.d.ts.map +1 -1
  71. package/dist/providers/openai/Capabilities.js +119 -122
  72. package/dist/providers/openai/Chat.d.ts.map +1 -1
  73. package/dist/providers/openai/Chat.js +19 -17
  74. package/dist/providers/openai/Embedding.d.ts.map +1 -1
  75. package/dist/providers/openai/Embedding.js +2 -1
  76. package/dist/providers/openai/Image.d.ts.map +1 -1
  77. package/dist/providers/openai/Image.js +2 -1
  78. package/dist/providers/openai/ModelDefinitions.d.ts +1 -24
  79. package/dist/providers/openai/ModelDefinitions.d.ts.map +1 -1
  80. package/dist/providers/openai/ModelDefinitions.js +1 -211
  81. package/dist/providers/openai/Models.d.ts +1 -0
  82. package/dist/providers/openai/Models.d.ts.map +1 -1
  83. package/dist/providers/openai/Models.js +46 -22
  84. package/dist/providers/openai/Moderation.d.ts.map +1 -1
  85. package/dist/providers/openai/Moderation.js +2 -1
  86. package/dist/providers/openai/Streaming.d.ts.map +1 -1
  87. package/dist/providers/openai/Streaming.js +5 -1
  88. package/dist/providers/openai/Transcription.d.ts.map +1 -1
  89. package/dist/providers/openai/Transcription.js +3 -2
  90. package/dist/providers/openai/index.d.ts.map +1 -1
  91. package/dist/providers/openai/index.js +2 -1
  92. package/dist/providers/openai/utils.d.ts +20 -0
  93. package/dist/providers/openai/utils.d.ts.map +1 -0
  94. package/dist/providers/openai/utils.js +25 -0
  95. package/dist/providers/registry.js +1 -1
  96. package/dist/utils/FileLoader.d.ts.map +1 -1
  97. package/dist/utils/FileLoader.js +1 -0
  98. package/package.json +1 -1
@@ -12,12 +12,22 @@ export class GeminiStreaming {
12
12
  const temperature = Capabilities.normalizeTemperature(request.temperature, request.model);
13
13
  const url = `${this.baseUrl}/models/${request.model}:streamGenerateContent?alt=sse&key=${this.apiKey}`;
14
14
  const { contents, systemInstructionParts } = await GeminiChatUtils.convertMessages(request.messages);
15
+ const generationConfig = {
16
+ temperature: temperature ?? undefined,
17
+ maxOutputTokens: request.max_tokens,
18
+ };
19
+ if (request.response_format?.type === "json_object") {
20
+ generationConfig.responseMimeType = "application/json";
21
+ }
22
+ else if (request.response_format?.type === "json_schema") {
23
+ generationConfig.responseMimeType = "application/json";
24
+ if (request.response_format.json_schema?.schema) {
25
+ generationConfig.responseSchema = this.sanitizeSchema(request.response_format.json_schema.schema);
26
+ }
27
+ }
15
28
  const payload = {
16
29
  contents,
17
- generationConfig: {
18
- temperature: temperature ?? undefined,
19
- maxOutputTokens: request.max_tokens,
20
- },
30
+ generationConfig,
21
31
  };
22
32
  if (systemInstructionParts.length > 0) {
23
33
  payload.systemInstruction = { parts: systemInstructionParts };
@@ -67,4 +77,24 @@ export class GeminiStreaming {
67
77
  }
68
78
  }
69
79
  }
80
+ sanitizeSchema(schema) {
81
+ if (typeof schema !== "object" || schema === null)
82
+ return schema;
83
+ const sanitized = { ...schema };
84
+ // Remove unsupported fields
85
+ delete sanitized.additionalProperties;
86
+ delete sanitized.$schema;
87
+ delete sanitized.$id;
88
+ delete sanitized.definitions;
89
+ // Recursively sanitize
90
+ if (sanitized.properties) {
91
+ for (const key in sanitized.properties) {
92
+ sanitized.properties[key] = this.sanitizeSchema(sanitized.properties[key]);
93
+ }
94
+ }
95
+ if (sanitized.items) {
96
+ sanitized.items = this.sanitizeSchema(sanitized.items);
97
+ }
98
+ return sanitized;
99
+ }
70
100
  }
@@ -1,7 +1,4 @@
1
- import { ModelFamilyDefinition } from "./ModelDefinitions.js";
2
1
  export declare class Capabilities {
3
- static getFamily(modelId: string): string;
4
- static getDefinition(modelId: string): ModelFamilyDefinition;
5
2
  static getContextWindow(modelId: string): number | null;
6
3
  static getMaxOutputTokens(modelId: string): number | null;
7
4
  static supportsVision(modelId: string): boolean;
@@ -12,19 +9,14 @@ export declare class Capabilities {
12
9
  static supportsImageGeneration(modelId: string): boolean;
13
10
  static supportsTranscription(modelId: string): boolean;
14
11
  static supportsModeration(modelId: string): boolean;
15
- static getInputPrice(modelId: string): number;
16
- static getCachedInputPrice(modelId: string): number | undefined;
17
- static getOutputPrice(modelId: string): number;
18
- static getModelType(modelId: string): "embedding" | "audio" | "moderation" | "image" | "chat";
19
- static formatDisplayName(modelId: string): string;
20
- private static applySpecialFormatting;
21
- private static specialPrefixFormat;
22
- static normalizeTemperature(temperature: number | undefined, modelId: string): number | undefined | null;
12
+ static getModelType(modelId: string): "embedding" | "audio" | "moderation" | "image" | "chat" | "audio_transcription" | "audio_speech";
23
13
  static getModalities(modelId: string): {
24
14
  input: string[];
25
15
  output: string[];
26
16
  };
27
17
  static getCapabilities(modelId: string): string[];
18
+ static normalizeTemperature(temperature: number | undefined, modelId: string): number | undefined | null;
19
+ static formatDisplayName(modelId: string): string;
28
20
  static getPricing(modelId: string): any;
29
21
  }
30
22
  //# sourceMappingURL=Capabilities.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Capabilities.d.ts","sourceRoot":"","sources":["../../../src/providers/openai/Capabilities.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAE7E,qBAAa,YAAY;IACvB,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAUzC,MAAM,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,qBAAqB;IAK5D,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAIvD,MAAM,CAAC,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAIzD,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAI/C,MAAM,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAI9C,MAAM,CAAC,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAIzD,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAIjD,MAAM,CAAC,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAInD,MAAM,CAAC,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAIxD,MAAM,CAAC,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAMtD,MAAM,CAAC,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAInD,MAAM,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAK7C,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI/D,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAK9C,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,GAAG,OAAO,GAAG,YAAY,GAAG,OAAO,GAAG,MAAM;IAI7F,MAAM,CAAC,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAKjD,OAAO,CAAC,MAAM,CAAC,sBAAsB;IAarC,OAAO,CAAC,MAAM,CAAC,mBAAmB;IAUlC,MAAM,CAAC,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,IAAI;IAUxG,MAAM,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG;QAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE;IAqB5E,MAAM,CAAC,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE;IAiBjD,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM;CAyBlC"}
1
+ {"version":3,"file":"Capabilities.d.ts","sourceRoot":"","sources":["../../../src/providers/openai/Capabilities.ts"],"names":[],"mappings":"AAEA,qBAAa,YAAY;IACvB,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAUvD,MAAM,CAAC,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IASzD,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAO/C,MAAM,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAO9C,MAAM,CAAC,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAOzD,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAIjD,MAAM,CAAC,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAOnD,MAAM,CAAC,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAOxD,MAAM,CAAC,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAOtD,MAAM,CAAC,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAOnD,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,GAAG,OAAO,GAAG,YAAY,GAAG,OAAO,GAAG,MAAM,GAAG,qBAAqB,GAAG,cAAc;IAUtI,MAAM,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG;QAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE;IAiB5E,MAAM,CAAC,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE;IAoBjD,MAAM,CAAC,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,IAAI;IAMxG,MAAM,CAAC,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAOjD,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,GAAG;CAcxC"}
@@ -1,160 +1,157 @@
1
- import { OPENAI_MODELS } from "./ModelDefinitions.js";
1
+ import { ModelRegistry } from "../../models/ModelRegistry.js";
2
2
  export class Capabilities {
3
- static getFamily(modelId) {
4
- for (const [key, def] of Object.entries(OPENAI_MODELS)) {
5
- if (key === "other")
6
- continue;
7
- if (def.pattern.test(modelId)) {
8
- return key;
9
- }
10
- }
11
- return "other";
12
- }
13
- static getDefinition(modelId) {
14
- const family = this.getFamily(modelId);
15
- return OPENAI_MODELS[family];
16
- }
17
3
  static getContextWindow(modelId) {
18
- return this.getDefinition(modelId).contextWindow;
4
+ const val = ModelRegistry.getContextWindow(modelId, "openai");
5
+ if (val)
6
+ return val;
7
+ if (/gpt-4.*(preview|turbo|vision|o)/.test(modelId) || /o1|o3/.test(modelId))
8
+ return 128_000;
9
+ if (/gpt-4/.test(modelId))
10
+ return 8_192;
11
+ if (/gpt-3\.5/.test(modelId))
12
+ return 16_385;
13
+ return 128_000;
19
14
  }
20
15
  static getMaxOutputTokens(modelId) {
21
- return this.getDefinition(modelId).maxOutputTokens;
16
+ const val = ModelRegistry.getMaxOutputTokens(modelId, "openai");
17
+ if (val)
18
+ return val;
19
+ if (/o1.*(pro|mini)|o3/.test(modelId))
20
+ return 65_536;
21
+ if (/gpt-4o/.test(modelId))
22
+ return 16_384;
23
+ return 4_096;
22
24
  }
23
25
  static supportsVision(modelId) {
24
- return !!this.getDefinition(modelId).features.vision;
26
+ const model = ModelRegistry.find(modelId, "openai");
27
+ if (model?.modalities?.input?.includes("image"))
28
+ return true;
29
+ return /gpt-4(?!-3)|o1/.test(modelId) && !/audio|realtime|voice/.test(modelId);
25
30
  }
26
31
  static supportsTools(modelId) {
27
- return !!this.getDefinition(modelId).features.tools;
32
+ const model = ModelRegistry.find(modelId, "openai");
33
+ if (model?.capabilities?.includes("function_calling"))
34
+ return true;
35
+ return !/embedding|moderation|dall-e|tts|whisper/.test(modelId);
28
36
  }
29
37
  static supportsStructuredOutput(modelId) {
30
- return !!this.getDefinition(modelId).features.structuredOutput;
38
+ const model = ModelRegistry.find(modelId, "openai");
39
+ if (model?.capabilities?.includes("structured_output"))
40
+ return true;
41
+ return /gpt-4|o1|o3/.test(modelId);
31
42
  }
32
43
  static supportsJsonMode(modelId) {
33
44
  return this.supportsStructuredOutput(modelId);
34
45
  }
35
46
  static supportsEmbeddings(modelId) {
36
- return this.getDefinition(modelId).type === "embedding";
47
+ const model = ModelRegistry.find(modelId, "openai");
48
+ if (model?.modalities?.output?.includes("embeddings"))
49
+ return true;
50
+ return /embedding/.test(modelId);
37
51
  }
38
52
  static supportsImageGeneration(modelId) {
39
- return this.getDefinition(modelId).type === "image";
53
+ const model = ModelRegistry.find(modelId, "openai");
54
+ if (model?.capabilities?.includes("image_generation") || model?.modalities?.output?.includes("image"))
55
+ return true;
56
+ return /dall-e|image/.test(modelId);
40
57
  }
41
58
  static supportsTranscription(modelId) {
42
- // Transcription is supported by audio models or specific models like gpt-4o-audio
43
- const def = this.getDefinition(modelId);
44
- return def.type === "audio" || (def.type === "chat" && /audio|transcribe/.test(modelId));
59
+ const model = ModelRegistry.find(modelId, "openai");
60
+ if (model?.modalities?.input?.includes("audio"))
61
+ return true;
62
+ return /whisper|audio|transcribe/.test(modelId);
45
63
  }
46
64
  static supportsModeration(modelId) {
47
- return this.getDefinition(modelId).type === "moderation";
48
- }
49
- static getInputPrice(modelId) {
50
- const prices = this.getDefinition(modelId).pricing;
51
- return prices.input || prices.price || 0.5;
52
- }
53
- static getCachedInputPrice(modelId) {
54
- return this.getDefinition(modelId).pricing.cached_input;
55
- }
56
- static getOutputPrice(modelId) {
57
- const prices = this.getDefinition(modelId).pricing;
58
- return prices.output || prices.price || 1.5;
65
+ const model = ModelRegistry.find(modelId, "openai");
66
+ if (model?.modalities?.output?.includes("moderation"))
67
+ return true;
68
+ return /moderation/.test(modelId);
59
69
  }
60
70
  static getModelType(modelId) {
61
- return this.getDefinition(modelId).type;
71
+ if (/moderation/.test(modelId))
72
+ return "moderation";
73
+ if (/embedding/.test(modelId))
74
+ return "embedding";
75
+ if (/dall-e|image/.test(modelId))
76
+ return "image";
77
+ if (/whisper|transcribe/.test(modelId))
78
+ return "audio_transcription";
79
+ if (/tts|speech/.test(modelId))
80
+ return "audio_speech";
81
+ if (/audio/.test(modelId))
82
+ return "audio";
83
+ return "chat";
62
84
  }
63
- static formatDisplayName(modelId) {
64
- const humanized = modelId.replace(/-/g, " ").split(" ").map(s => s.charAt(0).toUpperCase() + s.slice(1)).join(" ");
65
- return this.applySpecialFormatting(humanized);
66
- }
67
- static applySpecialFormatting(name) {
68
- return name
69
- .replace(/(\d{4}) (\d{2}) (\d{2})/, "$1$2$3")
70
- .replace(/^(?:Gpt|Chatgpt|Tts|Dall E) /g, (m) => this.specialPrefixFormat(m.trim()))
71
- .replace(/^O([13]) /g, "O$1-")
72
- .replace(/^O[13] Mini/g, (m) => m.replace(" ", "-"))
73
- .replace(/\d\.\d /g, (m) => m.replace(" ", "-"))
74
- .replace(/4o (?=Mini|Preview|Turbo|Audio|Realtime|Transcribe|Tts)/g, "4o-")
75
- .replace(/\bHd\b/g, "HD")
76
- .replace(/(?:Omni|Text) Moderation/g, (m) => m.replace(" ", "-"))
77
- .replace("Text Embedding", "text-embedding-");
78
- }
79
- static specialPrefixFormat(prefix) {
80
- switch (prefix) {
81
- case "Gpt": return "GPT-";
82
- case "Chatgpt": return "ChatGPT-";
83
- case "Tts": return "TTS-";
84
- case "Dall E": return "DALL-E-";
85
- default: return prefix + "-";
85
+ static getModalities(modelId) {
86
+ const input = ["text"];
87
+ const output = ["text"];
88
+ const model = ModelRegistry.find(modelId, "openai");
89
+ if (model?.modalities)
90
+ return model.modalities;
91
+ if (this.supportsVision(modelId))
92
+ input.push("image", "pdf");
93
+ if (this.supportsTranscription(modelId))
94
+ input.push("audio");
95
+ if (this.supportsImageGeneration(modelId))
96
+ output.push("image");
97
+ if (this.supportsEmbeddings(modelId))
98
+ output.push("embeddings");
99
+ if (this.supportsModeration(modelId))
100
+ output.push("moderation");
101
+ return { input, output };
102
+ }
103
+ static getCapabilities(modelId) {
104
+ const caps = ["streaming"];
105
+ const model = ModelRegistry.find(modelId, "openai");
106
+ if (model) {
107
+ model.capabilities.forEach(c => { if (!caps.includes(c))
108
+ caps.push(c); });
109
+ return caps;
86
110
  }
111
+ if (this.supportsTools(modelId))
112
+ caps.push("function_calling");
113
+ if (this.supportsStructuredOutput(modelId))
114
+ caps.push("structured_output");
115
+ if (this.supportsEmbeddings(modelId))
116
+ caps.push("batch");
117
+ if (/o\d|gpt-5/.test(modelId))
118
+ caps.push("reasoning");
119
+ if (this.supportsImageGeneration(modelId))
120
+ caps.push("image_generation");
121
+ if (this.supportsTranscription(modelId))
122
+ caps.push("transcription");
123
+ return caps;
87
124
  }
88
125
  static normalizeTemperature(temperature, modelId) {
89
- if (/^(o\d|gpt-5)/.test(modelId)) {
126
+ if (/^(o\d|gpt-5)/.test(modelId))
90
127
  return 1.0;
91
- }
92
- if (/-search/.test(modelId)) {
128
+ if (/-search/.test(modelId))
93
129
  return null;
94
- }
95
130
  return temperature;
96
131
  }
97
- static getModalities(modelId) {
98
- const type = this.getModelType(modelId);
99
- const features = this.getDefinition(modelId).features;
100
- const modalities = {
101
- input: ["text"],
102
- output: ["text"]
103
- };
104
- if (features.vision)
105
- modalities.input.push("image", "pdf");
106
- if (type === "audio") {
107
- modalities.input.push("audio");
108
- modalities.output.push("audio");
109
- }
110
- if (type === "image")
111
- modalities.output.push("image");
112
- if (type === "embedding")
113
- modalities.output.push("embeddings");
114
- if (type === "moderation")
115
- modalities.output.push("moderation");
116
- return modalities;
117
- }
118
- static getCapabilities(modelId) {
119
- const capabilities = [];
120
- const type = this.getModelType(modelId);
121
- const features = this.getDefinition(modelId).features;
122
- if (type !== "moderation" && type !== "embedding")
123
- capabilities.push("streaming");
124
- if (features.tools)
125
- capabilities.push("function_calling");
126
- if (features.structuredOutput)
127
- capabilities.push("structured_output");
128
- if (type === "embedding")
129
- capabilities.push("batch");
130
- if (/o\d|gpt-5|codex/.test(modelId))
131
- capabilities.push("reasoning");
132
- if (type === "image")
133
- capabilities.push("image_generation");
134
- if (type === "audio")
135
- capabilities.push("speech_generation", "transcription");
136
- return capabilities;
132
+ static formatDisplayName(modelId) {
133
+ const model = ModelRegistry.find(modelId, "openai");
134
+ if (model?.name && model.name !== modelId)
135
+ return model.name;
136
+ return modelId.replace(/-/g, " ").replace(/\b\w/g, c => c.toUpperCase());
137
137
  }
138
138
  static getPricing(modelId) {
139
- const standardPricing = {
140
- input_per_million: this.getInputPrice(modelId),
141
- output_per_million: this.getOutputPrice(modelId)
142
- };
143
- const cachedPrice = this.getCachedInputPrice(modelId);
144
- const pricing = {
139
+ const model = ModelRegistry.find(modelId, "openai");
140
+ if (model?.pricing)
141
+ return model.pricing;
142
+ let input = 2.5, output = 10.0;
143
+ if (/gpt-3/.test(modelId)) {
144
+ input = 0.5;
145
+ output = 1.5;
146
+ }
147
+ if (/mini/.test(modelId)) {
148
+ input = 0.15;
149
+ output = 0.6;
150
+ }
151
+ return {
145
152
  text_tokens: {
146
- standard: {
147
- ...standardPricing,
148
- ...(cachedPrice ? { cached_input_per_million: cachedPrice } : {})
149
- }
153
+ standard: { input_per_million: input, output_per_million: output }
150
154
  }
151
155
  };
152
- if (this.getModelType(modelId) === "embedding") {
153
- pricing.text_tokens.batch = {
154
- input_per_million: standardPricing.input_per_million * 0.5,
155
- output_per_million: standardPricing.output_per_million * 0.5
156
- };
157
- }
158
- return pricing;
159
156
  }
160
157
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Chat.d.ts","sourceRoot":"","sources":["../../../src/providers/openai/Chat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAK3D,qBAAa,UAAU;IACT,OAAO,CAAC,QAAQ,CAAC,OAAO;IAAU,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAxC,OAAO,EAAE,MAAM,EAAmB,MAAM,EAAE,MAAM;IAEvE,OAAO,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;CA0D3D"}
1
+ {"version":3,"file":"Chat.d.ts","sourceRoot":"","sources":["../../../src/providers/openai/Chat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAS,MAAM,gBAAgB,CAAC;AAOlE,qBAAa,UAAU;IACT,OAAO,CAAC,QAAQ,CAAC,OAAO;IAAU,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAxC,OAAO,EAAE,MAAM,EAAmB,MAAM,EAAE,MAAM;IAEvE,OAAO,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;CAsD3D"}
@@ -1,5 +1,7 @@
1
1
  import { Capabilities } from "./Capabilities.js";
2
2
  import { handleOpenAIError } from "./Errors.js";
3
+ import { ModelRegistry } from "../../models/ModelRegistry.js";
4
+ import { buildUrl } from "./utils.js";
3
5
  export class OpenAIChat {
4
6
  baseUrl;
5
7
  apiKey;
@@ -9,25 +11,24 @@ export class OpenAIChat {
9
11
  }
10
12
  async execute(request) {
11
13
  const temperature = Capabilities.normalizeTemperature(request.temperature, request.model);
14
+ const { model, messages, tools, temperature: _, max_tokens, response_format, headers, ...rest } = request;
12
15
  const body = {
13
- model: request.model,
14
- messages: request.messages,
16
+ model,
17
+ messages,
18
+ ...rest
15
19
  };
16
- if (temperature !== undefined) {
17
- if (temperature !== null) {
18
- body.temperature = temperature;
19
- }
20
+ if (temperature !== undefined && temperature !== null)
21
+ body.temperature = temperature;
22
+ if (max_tokens)
23
+ body.max_tokens = max_tokens;
24
+ if (tools)
25
+ body.tools = tools;
26
+ if (response_format)
27
+ body.response_format = response_format;
28
+ if (process.env.NODELLM_DEBUG === "true") {
29
+ console.log(`[OpenAI Request] ${JSON.stringify(body, null, 2)}`);
20
30
  }
21
- if (request.max_tokens) {
22
- body.max_tokens = request.max_tokens;
23
- }
24
- if (request.tools) {
25
- body.tools = request.tools;
26
- }
27
- if (request.response_format) {
28
- body.response_format = request.response_format;
29
- }
30
- const response = await fetch(`${this.baseUrl}/chat/completions`, {
31
+ const response = await fetch(buildUrl(this.baseUrl, '/chat/completions'), {
31
32
  method: "POST",
32
33
  headers: {
33
34
  "Authorization": `Bearer ${this.apiKey}`,
@@ -52,6 +53,7 @@ export class OpenAIChat {
52
53
  if (!content && !tool_calls) {
53
54
  throw new Error("OpenAI returned empty response");
54
55
  }
55
- return { content, tool_calls, usage };
56
+ const calculatedUsage = usage ? ModelRegistry.calculateCost(usage, model, "openai") : undefined;
57
+ return { content, tool_calls, usage: calculatedUsage };
56
58
  }
57
59
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Embedding.d.ts","sourceRoot":"","sources":["../../../src/providers/openai/Embedding.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAKtE,qBAAa,eAAe;IAExB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBADN,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM;IAG3B,OAAO,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,iBAAiB,CAAC;CA8CrE"}
1
+ {"version":3,"file":"Embedding.d.ts","sourceRoot":"","sources":["../../../src/providers/openai/Embedding.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAMtE,qBAAa,eAAe;IAExB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBADN,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM;IAG3B,OAAO,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,iBAAiB,CAAC;CA8CrE"}
@@ -1,6 +1,7 @@
1
1
  import { handleOpenAIError } from "./Errors.js";
2
2
  import { Capabilities } from "./Capabilities.js";
3
3
  import { DEFAULT_MODELS } from "../../constants.js";
4
+ import { buildUrl } from "./utils.js";
4
5
  export class OpenAIEmbedding {
5
6
  baseUrl;
6
7
  apiKey;
@@ -24,7 +25,7 @@ export class OpenAIEmbedding {
24
25
  if (request.user) {
25
26
  body.user = request.user;
26
27
  }
27
- const response = await fetch(`${this.baseUrl}/embeddings`, {
28
+ const response = await fetch(buildUrl(this.baseUrl, '/embeddings'), {
28
29
  method: "POST",
29
30
  headers: {
30
31
  "Authorization": `Bearer ${this.apiKey}`,
@@ -1 +1 @@
1
- {"version":3,"file":"Image.d.ts","sourceRoot":"","sources":["../../../src/providers/openai/Image.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAG7D,qBAAa,WAAW;IACV,OAAO,CAAC,QAAQ,CAAC,OAAO;IAAU,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAxC,OAAO,EAAE,MAAM,EAAmB,MAAM,EAAE,MAAM;IAEvE,OAAO,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC;CAmC7D"}
1
+ {"version":3,"file":"Image.d.ts","sourceRoot":"","sources":["../../../src/providers/openai/Image.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAI7D,qBAAa,WAAW;IACV,OAAO,CAAC,QAAQ,CAAC,OAAO;IAAU,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAxC,OAAO,EAAE,MAAM,EAAmB,MAAM,EAAE,MAAM;IAEvE,OAAO,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC;CAmC7D"}
@@ -1,4 +1,5 @@
1
1
  import { handleOpenAIError } from "./Errors.js";
2
+ import { buildUrl } from "./utils.js";
2
3
  export class OpenAIImage {
3
4
  baseUrl;
4
5
  apiKey;
@@ -14,7 +15,7 @@ export class OpenAIImage {
14
15
  quality: request.quality || "standard",
15
16
  n: request.n || 1,
16
17
  };
17
- const response = await fetch(`${this.baseUrl}/images/generations`, {
18
+ const response = await fetch(buildUrl(this.baseUrl, '/images/generations'), {
18
19
  method: "POST",
19
20
  headers: {
20
21
  "Authorization": `Bearer ${this.apiKey}`,
@@ -1,25 +1,2 @@
1
- export interface ModelPricing {
2
- input?: number;
3
- output?: number;
4
- price?: number;
5
- cached_input?: number;
6
- audio_input?: number;
7
- audio_output?: number;
8
- }
9
- export interface ModelFeatures {
10
- vision?: boolean;
11
- tools?: boolean;
12
- structuredOutput?: boolean;
13
- jsonMode?: boolean;
14
- }
15
- export type ModelType = "chat" | "embedding" | "audio" | "image" | "moderation";
16
- export interface ModelFamilyDefinition {
17
- pattern: RegExp;
18
- contextWindow: number | null;
19
- maxOutputTokens: number | null;
20
- pricing: ModelPricing;
21
- features: ModelFeatures;
22
- type: ModelType;
23
- }
24
- export declare const OPENAI_MODELS: Record<string, ModelFamilyDefinition>;
1
+ export {};
25
2
  //# sourceMappingURL=ModelDefinitions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ModelDefinitions.d.ts","sourceRoot":"","sources":["../../../src/providers/openai/ModelDefinitions.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,WAAW,GAAG,OAAO,GAAG,OAAO,GAAG,YAAY,CAAC;AAEhF,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,OAAO,EAAE,YAAY,CAAC;IACtB,QAAQ,EAAE,aAAa,CAAC;IACxB,IAAI,EAAE,SAAS,CAAC;CACjB;AAED,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAkN/D,CAAC"}
1
+ {"version":3,"file":"ModelDefinitions.d.ts","sourceRoot":"","sources":["../../../src/providers/openai/ModelDefinitions.ts"],"names":[],"mappings":""}