diff --git a/.changeset/calm-clocks-switch.md b/.changeset/calm-clocks-switch.md new file mode 100644 index 0000000000..f43bdff7b1 --- /dev/null +++ b/.changeset/calm-clocks-switch.md @@ -0,0 +1,7 @@ +--- +"@comet/cms-api": patch +--- + +Migrate from deprecated `@azure/openai` package to `openai` + +See https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/migration-javascript for more information. diff --git a/demo/api/package.json b/demo/api/package.json index 8f4a609134..2492859754 100644 --- a/demo/api/package.json +++ b/demo/api/package.json @@ -20,8 +20,8 @@ "mikro-orm": "mikro-orm", "mikro-orm:drop": "mikro-orm schema:drop -r", "mikro-orm:migration:generate": "mikro-orm migration:create", - "start": "$npm_execpath prebuild && dotenv -c -e .env.site-configs -- nest start --debug --watch --preserveWatchOutput", - "start:dev": "$npm_execpath prebuild && $npm_execpath db:migrate && $npm_execpath console createBlockIndexViews && NODE_OPTIONS='--max-old-space-size=512' dotenv -c -e .env.site-configs -- nest start --debug --watch --preserveWatchOutput", + "start": "$npm_execpath prebuild && dotenv -e .env.secrets -e .env.local -e .env -e .env.site-configs -- nest start --debug --watch --preserveWatchOutput", + "start:dev": "$npm_execpath prebuild && $npm_execpath db:migrate && $npm_execpath console createBlockIndexViews && NODE_OPTIONS='--max-old-space-size=512' dotenv -e .env.secrets -e .env.local -e .env -e .env.site-configs -- nest start --debug --watch --preserveWatchOutput", "start:prod": "node dist/main" }, "dependencies": { diff --git a/packages/api/cms-api/package.json b/packages/api/cms-api/package.json index e5f10d93f7..2cf8b3708f 100644 --- a/packages/api/cms-api/package.json +++ b/packages/api/cms-api/package.json @@ -34,7 +34,6 @@ "dependencies": { "@aws-sdk/client-s3": "3.645.0", "@azure-rest/ai-translation-text": "^1.0.0-beta.1", - "@azure/openai": "1.0.0-beta.11", "@azure/storage-blob": "^12.23.0", "@comet/blocks-api": "workspace:^7.10.0", "@fast-csv/parse": "^4.3.6", @@ -69,6 +68,7 @@ "mime-db": "^1.0.0", "multer": "^1.4.4", "node-fetch": "^2.0.0", + "openai": "^4.77.3", "passport": "^0.6.0", "passport-custom": "^1.1.1", "passport-http": "^0.3.0", diff --git a/packages/api/cms-api/src/content-generation/azure-open-ai/azure-open-ai-content-generation.service.ts b/packages/api/cms-api/src/content-generation/azure-open-ai/azure-open-ai-content-generation.service.ts index 15a2ccade0..508df361a5 100644 --- a/packages/api/cms-api/src/content-generation/azure-open-ai/azure-open-ai-content-generation.service.ts +++ b/packages/api/cms-api/src/content-generation/azure-open-ai/azure-open-ai-content-generation.service.ts @@ -1,5 +1,6 @@ -import { AzureKeyCredential, ChatRequestMessage, OpenAIClient } from "@azure/openai"; import { Inject, Injectable } from "@nestjs/common"; +import { AzureOpenAI } from "openai"; +import { ChatCompletionMessageParam } from "openai/resources/chat/completions"; import { FilesService } from "../../dam/files/files.service"; import { ContentGenerationServiceInterface } from "../content-generation-service.interface"; @@ -11,6 +12,7 @@ export type AzureOpenAiConfig = { deploymentId: string; apiKey: string; apiUrl: string; + apiVersion?: string; }; type ConfigByMethod = Partial>; @@ -40,6 +42,15 @@ export class AzureOpenAiContentGenerationService { return config; } + private createClient(config: AzureOpenAiConfig): AzureOpenAI { + return new AzureOpenAI({ + apiKey: config.apiKey, + deployment: config.deploymentId, + apiVersion: config.apiVersion ?? "2024-03-01-preview", + endpoint: config.apiUrl, + }); + } + async generateAltText(fileId: string): Promise { const config = this.getConfigForMethod("generateAltText"); @@ -49,8 +60,8 @@ export class AzureOpenAiContentGenerationService { throw new Error("File doesn't exist"); } - const client = new OpenAIClient(config.apiUrl, new AzureKeyCredential(config.apiKey)); - const prompt: ChatRequestMessage[] = [ + const client = this.createClient(config); + const prompt: Array = [ { role: "system", content: @@ -61,7 +72,7 @@ export class AzureOpenAiContentGenerationService { content: [ { type: "image_url", - imageUrl: { + image_url: { url: await this.filesService.getFileAsBase64String(file), detail: "low", }, @@ -69,7 +80,7 @@ export class AzureOpenAiContentGenerationService { ], }, ]; - const result = await client.getChatCompletions(config.deploymentId, prompt, { maxTokens: 300 }); + const result = await client.chat.completions.create({ messages: prompt, model: "", max_tokens: 300 }); return result.choices[0].message?.content ?? ""; } @@ -82,8 +93,8 @@ export class AzureOpenAiContentGenerationService { throw new Error("File doesn't exist"); } - const client = new OpenAIClient(config.apiUrl, new AzureKeyCredential(config.apiKey)); - const prompt: ChatRequestMessage[] = [ + const client = this.createClient(config); + const prompt: Array = [ { role: "system", content: @@ -94,7 +105,7 @@ export class AzureOpenAiContentGenerationService { content: [ { type: "image_url", - imageUrl: { + image_url: { url: await this.filesService.getFileAsBase64String(file), detail: "low", }, @@ -102,7 +113,7 @@ export class AzureOpenAiContentGenerationService { ], }, ]; - const result = await client.getChatCompletions(config.deploymentId, prompt, { maxTokens: 300 }); + const result = await client.chat.completions.create({ messages: prompt, model: "", max_tokens: 300 }); return result.choices[0].message?.content ?? ""; } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a8e00c4dbd..611d13afbe 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2300,9 +2300,6 @@ importers: '@azure-rest/ai-translation-text': specifier: ^1.0.0-beta.1 version: 1.0.0 - '@azure/openai': - specifier: 1.0.0-beta.11 - version: 1.0.0-beta.11 '@azure/storage-blob': specifier: ^12.23.0 version: 12.24.0 @@ -2405,6 +2402,9 @@ importers: node-fetch: specifier: ^2.0.0 version: 2.6.8 + openai: + specifier: ^4.77.3 + version: 4.77.3 passport: specifier: ^0.6.0 version: 0.6.0 @@ -3479,7 +3479,7 @@ packages: resolution: {integrity: sha512-xhlTqH0m31mnsG0tIP4ETgfSB6gXDaYYsUWTrlUV93fFQPI9dd8hE0Ot6MHLCtqgB32hwJAC3YZMWlXZw7AleA==} engines: {node: '>=14'} dependencies: - node-fetch: 2.6.8 + node-fetch: 2.7.0 transitivePeerDependencies: - encoding dev: true @@ -4120,7 +4120,7 @@ packages: '@azure/core-auth': 1.4.0 '@azure/core-rest-pipeline': 1.16.3 '@azure/core-tracing': 1.1.2 - '@azure/core-util': 1.1.1 + '@azure/core-util': 1.9.2 tslib: 2.8.1 transitivePeerDependencies: - supports-color @@ -4206,13 +4206,6 @@ packages: - supports-color dev: false - /@azure/core-sse@2.1.2: - resolution: {integrity: sha512-yf+pFIu8yCzXu9RbH2+8kp9vITIKJLHgkLgFNA6hxiDHK3fxeP596cHUj4c8Cm8JlooaUnYdHmF84KCZt3jbmw==} - engines: {node: '>=18.0.0'} - dependencies: - tslib: 2.8.1 - dev: false - /@azure/core-tracing@1.1.2: resolution: {integrity: sha512-dawW9ifvWAWmUm9/h+/UQ2jrdvjCJ7VJEuCJ6XVNudzcOwm53BFZH4Q845vjfgoUAM8ZxokvVNxNxAITc502YA==} engines: {node: '>=18.0.0'} @@ -4220,14 +4213,6 @@ packages: tslib: 2.8.1 dev: false - /@azure/core-util@1.1.1: - resolution: {integrity: sha512-A4TBYVQCtHOigFb2ETiiKFDocBoI1Zk2Ui1KpI42aJSIDexF7DHQFpnjonltXAIU/ceH+1fsZAWWgvX6/AKzog==} - engines: {node: '>=12.0.0'} - dependencies: - '@azure/abort-controller': 1.1.0 - tslib: 2.8.1 - dev: false - /@azure/core-util@1.9.2: resolution: {integrity: sha512-l1Qrqhi4x1aekkV+OlcqsJa4AnAkj5p0JV8omgwjaV9OAbP41lvrMvs+CptfetKkeEaGRGSzby7sjPZEX7+kkQ==} engines: {node: '>=18.0.0'} @@ -4251,21 +4236,6 @@ packages: tslib: 2.8.1 dev: false - /@azure/openai@1.0.0-beta.11: - resolution: {integrity: sha512-OXS27xkG1abiGf5VZUKnkJKr1VCo8+6EUrTGW5aSVjc5COqX8jAUqVAOZsQVCHBdtWYSBULlZkc0ncKMTRQAiQ==} - engines: {node: '>=18.0.0'} - dependencies: - '@azure-rest/core-client': 1.4.0 - '@azure/core-auth': 1.4.0 - '@azure/core-rest-pipeline': 1.16.3 - '@azure/core-sse': 2.1.2 - '@azure/core-util': 1.9.2 - '@azure/logger': 1.0.3 - tslib: 2.4.1 - transitivePeerDependencies: - - supports-color - dev: false - /@azure/storage-blob@12.24.0: resolution: {integrity: sha512-l8cmWM4C7RoNCBOImoFMxhTXe1Lr+8uQ/IgnhRNMpfoA9bAFWoLG4XrWm6O5rKXortreVQuD+fc1hbzWklOZbw==} engines: {node: '>=18.0.0'} @@ -12625,7 +12595,7 @@ packages: dependencies: chalk: 4.1.2 consola: 2.15.3 - node-fetch: 2.6.8 + node-fetch: 2.7.0 transitivePeerDependencies: - encoding @@ -16081,6 +16051,13 @@ packages: '@types/node': 22.10.5 dev: true + /@types/node-fetch@2.6.12: + resolution: {integrity: sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==} + dependencies: + '@types/node': 22.10.5 + form-data: 4.0.0 + dev: false + /@types/node-fetch@2.6.2: resolution: {integrity: sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==} dependencies: @@ -16966,7 +16943,6 @@ packages: engines: {node: '>=6.5'} dependencies: event-target-shim: 5.0.1 - dev: true /accepts@1.3.8: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} @@ -17328,7 +17304,7 @@ packages: engines: {node: '>=12.0'} deprecated: The `apollo-server-env` package is part of Apollo Server v2 and v3, which are now deprecated (end-of-life October 22nd 2023). This package's functionality is now found in the `@apollo/utils.fetcher` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details. dependencies: - node-fetch: 2.6.8 + node-fetch: 2.7.0 transitivePeerDependencies: - encoding dev: false @@ -22034,7 +22010,6 @@ packages: /event-target-shim@5.0.1: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} - dev: true /eventemitter3@3.1.2: resolution: {integrity: sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==} @@ -22717,7 +22692,6 @@ packages: /form-data-encoder@1.7.2: resolution: {integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==} - dev: true /form-data@2.3.3: resolution: {integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==} @@ -22750,7 +22724,6 @@ packages: asynckit: 0.4.0 combined-stream: 1.0.8 mime-types: 2.1.35 - dev: true /formdata-node@4.4.1: resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==} @@ -22758,7 +22731,6 @@ packages: dependencies: node-domexception: 1.0.0 web-streams-polyfill: 4.0.0-beta.3 - dev: true /forwarded-parse@2.1.2: resolution: {integrity: sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==} @@ -24819,7 +24791,7 @@ packages: /isomorphic-fetch@3.0.0: resolution: {integrity: sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==} dependencies: - node-fetch: 2.6.8 + node-fetch: 2.7.0 whatwg-fetch: 3.6.2 transitivePeerDependencies: - encoding @@ -27597,7 +27569,6 @@ packages: /node-domexception@1.0.0: resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} engines: {node: '>=10.5.0'} - dev: true /node-emoji@1.11.0: resolution: {integrity: sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==} @@ -27641,7 +27612,6 @@ packages: optional: true dependencies: whatwg-url: 5.0.0 - dev: false /node-forge@1.3.1: resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} @@ -27991,6 +27961,27 @@ packages: is-docker: 2.2.1 is-wsl: 2.2.0 + /openai@4.77.3: + resolution: {integrity: sha512-wLDy4+KWHz31HRFMW2+9KQuVuT2QWhs0z94w1Gm1h2Ut9vIHr9/rHZggbykZEfyiaJRVgw8ZS9K6AylDWzvPYw==} + hasBin: true + peerDependencies: + zod: ^3.23.8 + peerDependenciesMeta: + zod: + optional: true + dependencies: + '@types/node': 18.15.3 + '@types/node-fetch': 2.6.12 + abort-controller: 3.0.0 + agentkeepalive: 4.2.1 + form-data-encoder: 1.7.2 + formdata-node: 4.4.1 + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: false + /opener@1.5.2: resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==} hasBin: true @@ -32416,7 +32407,7 @@ packages: engines: {node: '>=8'} dependencies: buffer: 5.7.1 - node-fetch: 2.6.8 + node-fetch: 2.7.0 transitivePeerDependencies: - encoding dev: true @@ -34023,7 +34014,6 @@ packages: /web-streams-polyfill@4.0.0-beta.3: resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==} engines: {node: '>= 14'} - dev: true /web-worker@1.3.0: resolution: {integrity: sha512-BSR9wyRsy/KOValMgd5kMyr3JzpdeoR9KVId8u5GVlTTAtNChlsE4yTxeY7zMdNSyOmoKBv8NH2qeRY9Tg+IaA==}