From ecabebc4acc2b7139f1d31f14df0a478072889c5 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 19 Oct 2023 17:16:51 +0100 Subject: [PATCH 1/5] ci: update release secrets (#388) --- .github/workflows/create-releases.yml | 2 +- .github/workflows/publish-npm.yml | 2 +- .github/workflows/release-doctor.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/create-releases.yml b/.github/workflows/create-releases.yml index 7c09d64bb..2a5f9d49f 100644 --- a/.github/workflows/create-releases.yml +++ b/.github/workflows/create-releases.yml @@ -36,4 +36,4 @@ jobs: run: | bash ./bin/publish-npm env: - NPM_TOKEN: ${{ secrets.OPENAI_NPM_TOKEN }} + NPM_TOKEN: ${{ secrets.OPENAI_NPM_TOKEN || secrets.NPM_TOKEN }} diff --git a/.github/workflows/publish-npm.yml b/.github/workflows/publish-npm.yml index b52ec0a4e..326067066 100644 --- a/.github/workflows/publish-npm.yml +++ b/.github/workflows/publish-npm.yml @@ -25,4 +25,4 @@ jobs: run: | bash ./bin/publish-npm env: - NPM_TOKEN: ${{ secrets.OPENAI_NPM_TOKEN }} + NPM_TOKEN: ${{ secrets.OPENAI_NPM_TOKEN || secrets.NPM_TOKEN }} diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml index a94615c37..b640869d0 100644 --- a/.github/workflows/release-doctor.yml +++ b/.github/workflows/release-doctor.yml @@ -20,4 +20,4 @@ jobs: bash ./bin/check-release-environment env: STAINLESS_API_KEY: ${{ secrets.STAINLESS_API_KEY }} - NPM_TOKEN: ${{ secrets.OPENAI_NPM_TOKEN }} + NPM_TOKEN: ${{ secrets.OPENAI_NPM_TOKEN || secrets.NPM_TOKEN }} From cf70deaba1426786aba9b938d280c61aeb516e34 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 19 Oct 2023 21:09:49 +0100 Subject: [PATCH 2/5] feat(api): add embeddings encoding_format (#390) --- src/resources/embeddings.ts | 6 ++++++ tests/api-resources/embeddings.test.ts | 1 + 2 files changed, 7 insertions(+) diff --git a/src/resources/embeddings.ts b/src/resources/embeddings.ts index 9be063552..2c3302c99 100644 --- a/src/resources/embeddings.ts +++ b/src/resources/embeddings.ts @@ -97,6 +97,12 @@ export interface EmbeddingCreateParams { */ model: (string & {}) | 'text-embedding-ada-002'; + /** + * The format to return the embeddings in. Can be either `float` or + * [`base64`](https://pypi.org/project/pybase64/). + */ + encoding_format?: 'float' | 'base64'; + /** * A unique identifier representing your end-user, which can help OpenAI to monitor * and detect abuse. diff --git a/tests/api-resources/embeddings.test.ts b/tests/api-resources/embeddings.test.ts index 1e2af9297..24cb19482 100644 --- a/tests/api-resources/embeddings.test.ts +++ b/tests/api-resources/embeddings.test.ts @@ -27,6 +27,7 @@ describe('resource embeddings', () => { const response = await openai.embeddings.create({ input: 'The quick brown fox jumped over the lazy dog', model: 'text-embedding-ada-002', + encoding_format: 'float', user: 'user-1234', }); }); From 2dd005c1c497605036d3524f19d130b3fc5f8d8b Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 19 Oct 2023 22:27:07 +0100 Subject: [PATCH 3/5] feat: handle 204 No Content gracefully (#391) --- src/core.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/core.ts b/src/core.ts index 9cd99639d..75cb120c6 100644 --- a/src/core.ts +++ b/src/core.ts @@ -49,6 +49,11 @@ async function defaultParseResponse(props: APIResponseProps): Promise { return Stream.fromSSEResponse(response, props.controller) as any; } + // fetch refuses to read the body when the status code is 204. + if (response.status === 204) { + return null as T; + } + const contentType = response.headers.get('content-type'); if (contentType?.includes('application/json')) { const json = await response.json(); From a8c5d822beca6302f0795b51254a4759c0c6240f Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Sun, 22 Oct 2023 01:53:33 +0100 Subject: [PATCH 4/5] ci: reenable deno builds (#394) --- README.md | 6 + build-deno | 25 ++++ package.json | 3 +- scripts/denoify.ts | 229 ++++++++++++++++++++++++++++++++++++ scripts/git-publish-deno.sh | 63 ++++++++++ 5 files changed, 325 insertions(+), 1 deletion(-) create mode 100755 build-deno create mode 100644 scripts/denoify.ts create mode 100755 scripts/git-publish-deno.sh diff --git a/README.md b/README.md index 3ebda69ca..dbc3c698b 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,12 @@ npm install --save openai yarn add openai ``` +You can import in Deno via: + +```ts +import OpenAI from 'https://raw.githubusercontent.com/openai/openai-node/v4.12.4-deno/mod.ts'; +``` + ## Usage The full API of this library can be found in [api.md file](https://github.com/openai/openai-node/blob/master/api.md). The code below shows how to get started using the chat completions API. diff --git a/build-deno b/build-deno new file mode 100755 index 000000000..428ffa304 --- /dev/null +++ b/build-deno @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +set -exuo pipefail + +rm -rf deno; mkdir deno +cp -rp src/* README.md deno +rm deno/_shims/auto/*-node.ts +for dir in deno/_shims deno/_shims/auto; do + rm "${dir}"/*.{d.ts,js,mjs} + for file in "${dir}"/*-deno.ts; do + mv -- "$file" "${file%-deno.ts}.ts" + done +done +for file in LICENSE CHANGELOG.md; do + if [ -e "${file}" ]; then cp "${file}" deno; fi +done +npm exec ts-node -- scripts/denoify.ts +deno fmt deno +deno check deno/mod.ts +if [ -e deno_tests ]; then + deno test deno_tests --allow-env +fi + +# make sure that nothing crashes when we load the Deno module +(cd deno && deno run mod.ts) diff --git a/package.json b/package.json index 98531f013..d8b69f06a 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,8 @@ "format": "prettier --write --cache --cache-strategy metadata . !dist", "tsn": "ts-node -r tsconfig-paths/register", "lint": "eslint --ext ts,js .", - "fix": "eslint --fix --ext ts,js ." + "fix": "eslint --fix --ext ts,js .", + "postpublish": "bash scripts/git-publish-deno.sh" }, "dependencies": { "@types/node": "^18.11.18", diff --git a/scripts/denoify.ts b/scripts/denoify.ts new file mode 100644 index 000000000..9922b7bf8 --- /dev/null +++ b/scripts/denoify.ts @@ -0,0 +1,229 @@ +import path from 'path'; +import * as tm from 'ts-morph'; +import { name as pkgName } from '../package.json'; +import fs from 'fs'; + +const rootDir = path.resolve(__dirname, '..'); +const denoDir = path.join(rootDir, 'deno'); +const tsConfigFilePath = path.join(rootDir, 'tsconfig.deno.json'); + +async function denoify() { + const project = new tm.Project({ tsConfigFilePath }); + + for (const file of project.getSourceFiles()) { + if (!file.getFilePath().startsWith(denoDir + '/')) continue; + + let addedBuffer = false, + addedProcess = false; + file.forEachDescendant((node) => { + switch (node.getKind()) { + case tm.ts.SyntaxKind.ExportDeclaration: { + const decl: tm.ExportDeclaration = node as any; + if (decl.isTypeOnly()) return; + for (const named of decl.getNamedExports()) { + // Convert `export { Foo } from './foo.ts'` + // to `export { type Foo } from './foo.ts'` + // if `./foo.ts` only exports types for `Foo` + if (!named.isTypeOnly() && !hasValueDeclarations(named)) { + named.replaceWithText(`type ${named.getText()}`); + } + } + break; + } + case tm.ts.SyntaxKind.ImportEqualsDeclaration: { + const decl: tm.ImportEqualsDeclaration = node as any; + if (decl.isTypeOnly()) return; + + const ref = decl.getModuleReference(); + if (!hasValueDeclarations(ref)) { + const params = isBuiltinType(ref.getType()) ? [] : ref.getType().getTypeArguments(); + if (params.length) { + const paramsStr = params.map((p: tm.TypeParameter) => p.getText()).join(', '); + const bindingsStr = params + .map((p: tm.TypeParameter) => p.getSymbol()?.getName() || p.getText()) + .join(', '); + decl.replaceWithText( + `export type ${decl.getName()}<${paramsStr}> = ${ref.getText()}<${bindingsStr}>`, + ); + } else { + decl.replaceWithText(`export type ${decl.getName()} = ${ref.getText()}`); + } + } + break; + } + case tm.ts.SyntaxKind.Identifier: { + const id = node as tm.Identifier; + if (!addedBuffer && id.getText() === 'Buffer') { + addedBuffer = true; + file?.addVariableStatement({ + declarations: [ + { + name: 'Buffer', + type: 'any', + }, + ], + hasDeclareKeyword: true, + }); + file?.addTypeAlias({ + name: 'Buffer', + type: 'any', + }); + } + if (!addedProcess && id.getText() === 'process') { + addedProcess = true; + file?.addVariableStatement({ + declarations: [ + { + name: 'process', + type: 'any', + }, + ], + hasDeclareKeyword: true, + }); + } + } + } + }); + } + + await project.save(); + + for (const file of project.getSourceFiles()) { + if (!file.getFilePath().startsWith(denoDir + '/')) continue; + for (const decl of [...file.getImportDeclarations(), ...file.getExportDeclarations()]) { + const moduleSpecifier = decl.getModuleSpecifier(); + if (!moduleSpecifier) continue; + let specifier = moduleSpecifier.getLiteralValue().replace(/^node:/, ''); + if (!specifier || specifier.startsWith('http')) continue; + + if (nodeStdModules.has(specifier)) { + // convert node builtins to deno.land/std + specifier = `https://deno.land/std@0.177.0/node/${specifier}.ts`; + } else if (specifier.startsWith(pkgName + '/')) { + // convert self-referencing module specifiers to relative paths + specifier = file.getRelativePathAsModuleSpecifierTo(denoDir + specifier.substring(pkgName.length)); + } else if (specifier === 'qs') { + decl.replaceWithText(`import { qs } from "https://deno.land/x/deno_qs@0.0.1/mod.ts"`); + continue; + } else if (!decl.isModuleSpecifierRelative()) { + specifier = `npm:${specifier}`; + } + + if (specifier.startsWith('./') || specifier.startsWith('../')) { + // there may be CJS directory module specifiers that implicitly resolve + // to /index.ts. Add an explicit /index.ts to the end + const sourceFile = decl.getModuleSpecifierSourceFile(); + if (sourceFile && /\/index\.ts$/.test(sourceFile.getFilePath()) && !/\/mod\.ts$/.test(specifier)) { + if (/\/index(\.ts)?$/.test(specifier)) { + specifier = specifier.replace(/\/index(\.ts)?$/, '/mod.ts'); + } else { + specifier += '/mod.ts'; + } + } + // add explicit .ts file extensions to relative module specifiers + specifier = specifier.replace(/(\.[^./]*)?$/, '.ts'); + } + moduleSpecifier.replaceWithText(JSON.stringify(specifier)); + } + } + + await project.save(); + + await Promise.all( + project.getSourceFiles().map(async (f) => { + const filePath = f.getFilePath(); + if (filePath.endsWith('index.ts')) { + const newPath = filePath.replace(/index\.ts$/, 'mod.ts'); + await fs.promises.rename(filePath, newPath); + } + }), + ); +} + +const nodeStdModules = new Set([ + 'assert', + 'assertion_error', + 'async_hooks', + 'buffer', + 'child_process', + 'cluster', + 'console', + 'constants', + 'crypto', + 'dgram', + 'diagnostics_channel', + 'dns', + 'domain', + 'events', + 'fs', + 'global', + 'http', + 'http2', + 'https', + 'inspector', + 'module_all', + 'module_esm', + 'module', + 'net', + 'os', + 'path', + 'perf_hooks', + 'process', + 'punycode', + 'querystring', + 'readline', + 'repl', + 'stream', + 'string_decoder', + 'sys', + 'timers', + 'tls', + 'tty', + 'upstream_modules', + 'url', + 'util', + 'v8', + 'vm', + 'wasi', + 'worker_threads', + 'zlib', +]); + +const typeDeclarationKinds = new Set([ + tm.ts.SyntaxKind.InterfaceDeclaration, + tm.ts.SyntaxKind.ModuleDeclaration, + tm.ts.SyntaxKind.TypeAliasDeclaration, +]); + +const builtinTypeNames = new Set(['Array', 'Set', 'Map', 'Record', 'Promise']); + +function isBuiltinType(type: tm.Type): boolean { + const symbol = type.getSymbol(); + return ( + symbol != null && + builtinTypeNames.has(symbol.getName()) && + symbol.getDeclarations().some((d) => d.getSourceFile().getFilePath().includes('node_modules/typescript')) + ); +} + +function hasValueDeclarations(nodes?: tm.Node): boolean; +function hasValueDeclarations(nodes?: tm.Node[]): boolean; +function hasValueDeclarations(nodes?: tm.Node | tm.Node[]): boolean { + if (nodes && !Array.isArray(nodes)) { + return ( + !isBuiltinType(nodes.getType()) && hasValueDeclarations(nodes.getType().getSymbol()?.getDeclarations()) + ); + } + return nodes ? + nodes.some((n) => { + const parent = n.getParent(); + return ( + !typeDeclarationKinds.has(n.getKind()) && + // sometimes the node will be the right hand side of a type alias + (!parent || !typeDeclarationKinds.has(parent.getKind())) + ); + }) + : false; +} + +denoify(); diff --git a/scripts/git-publish-deno.sh b/scripts/git-publish-deno.sh new file mode 100755 index 000000000..81e0d9544 --- /dev/null +++ b/scripts/git-publish-deno.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash + +# This script pushes the contents of the `deno`` directory to the `deno` branch, +# and creates a `vx.x.x-deno` tag, so that Deno users can +# import OpenAI from "https://raw.githubusercontent.com/openai/openai-node/vx.x.x-deno/mod.ts" + +# It's also possible to publish to deno.land. You can do this by: +# - Creating a separate GitHub repo +# - Add the deno.land webhook to the repo as described at https://deno.com/add_module +# - Set the following environment variables when running this script: +# - DENO_PUSH_REMOTE_URL - the remote url of the separate GitHub repo +# - DENO_PUSH_BRANCH - the branch you want to push to in that repo (probably `main`) +# - DENO_PUSH_VERSION - defaults to version in package.json +# - DENO_PUSH_RELEASE_TAG - defaults to v$DENO_PUSH_VERSION-deno + +die () { + echo >&2 "$@" + exit 1 +} + +set -exuo pipefail + +# Allow caller to set the following environment variables, but provide defaults +# if unset +# : "${FOO:=bar}" sets FOO=bar unless it's set and non-empty +# https://stackoverflow.com/questions/307503/whats-a-concise-way-to-check-that-environment-variables-are-set-in-a-unix-shell +# https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html + +: "${DENO_PUSH_VERSION:=$(node -p 'require("./package.json").version')}" +: "${DENO_PUSH_BRANCH:=deno}" +: "${DENO_PUSH_REMOTE_URL:=$(git remote get-url origin)}" +: "${DENO_PUSH_RELEASE_TAG:="v$DENO_PUSH_VERSION-deno"}" + +if [ ! -e deno ]; then ./build; fi + +# We want to commit and push a branch where everything inside the deno +# directory is at root level in the branch. + +# We can do this by temporarily creating a git repository inside deno, +# committing files to the branch, and pushing it to the remote. + +cd deno +rm -rf .git +git init +git remote add origin "$DENO_PUSH_REMOTE_URL" +if git fetch origin "$DENO_PUSH_RELEASE_TAG"; then + die "Tag $DENO_PUSH_RELEASE_TAG already exists" +fi +if git fetch origin "$DENO_PUSH_BRANCH"; then + # the branch already exists on the remote; "check out" the branch without + # changing files in the working directory + git branch "$DENO_PUSH_BRANCH" -t origin/"$DENO_PUSH_BRANCH" + git symbolic-ref HEAD refs/heads/"$DENO_PUSH_BRANCH" + git reset +else + # the branch doesn't exist on the remote yet + git checkout -b "$DENO_PUSH_BRANCH" +fi +git add . +git commit -m "chore(deno): release $DENO_PUSH_VERSION" +git tag -a "$DENO_PUSH_RELEASE_TAG" -m "release $DENO_PUSH_VERSION" +git push --tags --set-upstream origin "$DENO_PUSH_BRANCH" +rm -rf .git From 05e2bf39a353166cc59633e98ab4270b9a43fd71 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Sun, 22 Oct 2023 01:53:50 +0100 Subject: [PATCH 5/5] release: 4.13.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 9 +++++++++ package.json | 2 +- src/version.ts | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index a9e8de513..4750060f4 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "4.12.4" + ".": "4.13.0" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 71d1f75d8..a84c16a20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 4.13.0 (2023-10-22) + +Full Changelog: [v4.12.4...v4.13.0](https://github.com/openai/openai-node/compare/v4.12.4...v4.13.0) + +### Features + +* **api:** add embeddings encoding_format ([#390](https://github.com/openai/openai-node/issues/390)) ([cf70dea](https://github.com/openai/openai-node/commit/cf70deaba1426786aba9b938d280c61aeb516e34)) +* handle 204 No Content gracefully ([#391](https://github.com/openai/openai-node/issues/391)) ([2dd005c](https://github.com/openai/openai-node/commit/2dd005c1c497605036d3524f19d130b3fc5f8d8b)) + ## 4.12.4 (2023-10-17) Full Changelog: [v4.12.3...v4.12.4](https://github.com/openai/openai-node/compare/v4.12.3...v4.12.4) diff --git a/package.json b/package.json index d8b69f06a..64da4e85d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "openai", - "version": "4.12.4", + "version": "4.13.0", "description": "Client library for the OpenAI API", "author": "OpenAI ", "types": "dist/index.d.ts", diff --git a/src/version.ts b/src/version.ts index d02576568..aa60823c4 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1 +1 @@ -export const VERSION = '4.12.4'; // x-release-please-version +export const VERSION = '4.13.0'; // x-release-please-version