Skip to content

Commit

Permalink
Merge pull request #1 from Kazuki-tam/feature/chatgpt
Browse files Browse the repository at this point in the history
Feature/chatgpt
  • Loading branch information
Kazuki-tam authored Mar 6, 2023
2 parents aa16528 + 9255ac4 commit ac8dcc3
Show file tree
Hide file tree
Showing 12 changed files with 518 additions and 92 deletions.
25 changes: 22 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# gas-gpt-starter

`gas-gpt-starter` is a starter kit to use GPT-3 in Google Apps Script.
You can clone [this sample sheet](https://docs.google.com/spreadsheets/d/1xYZbBp4TFuSxOfjsW0HJtDCS3UEI5nWYd2FfPxEoLnQ/edit?usp=sharing) if you want to use GPT-3 function immediately without deployment.
`gas-gpt-starter` is a starter kit to use GPT-3 and ChatGPT in Google Apps Script.
You can clone [this sample sheet](https://docs.google.com/spreadsheets/d/1xYZbBp4TFuSxOfjsW0HJtDCS3UEI5nWYd2FfPxEoLnQ/edit?usp=sharing) if you want to use GPT-3 and ChatGPT function immediately without deployment.

Note: You need to set the OpenAI API key into script properties even though you cloned the sample sheet.

Expand Down Expand Up @@ -74,6 +74,22 @@ Deploy your code to the existing project.
deno task deploy
```

### CHATGPT function
1. Authorize this project's script by execution
2. Use `=CHATGPT()` in your Google Workspace

You can add a system message with the second argument.

```
CHATGPT(prompt, system)
// Example 1 on Google Sheets
=CHATGPT("Hello, world!")
// Example 2 on Google Sheets
=CHATGPT(A1, "You are a helpful assistant.")
```

### GPT-3 function

1. Authorize this project's script by execution
Expand Down Expand Up @@ -122,6 +138,9 @@ Open the current directory's clasp project on script.google.com.
deno task open
```

## ChatGPT
`gpt-3.5-turbo` is supported in this project.

## GPT-3
You can use four main models with different levels of power suitable for different tasks.

Expand All @@ -130,7 +149,7 @@ You can use four main models with different levels of power suitable for differe
- text-babbage-001
- text-ada-001

[📖 Learn more GPT3](https://beta.openai.com/docs/models/gpt-3)
[📖 Learn more API reference](https://platform.openai.com/docs/api-reference/introduction)

## License
MIT
20 changes: 16 additions & 4 deletions _tasks/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,20 @@ import { GasPlugin } from "npm:esbuild-gas-plugin";
import { denoPlugin } from "https://deno.land/x/esbuild_deno_loader@0.6.0/mod.ts";
import { build, stop } from "https://deno.land/x/esbuild@v0.17.3/mod.js";

const buildOptions = {
entryPoints: ["src/main.ts"],
const gptBuildOptions = {
entryPoints: ["src/gpt.ts"],
minify: true,
bundle: true,
outfile: "dist/main.js",
outfile: "dist/gpt.js",
target: "es2020",
plugins: [denoPlugin(), GasPlugin],
};

const chatGptBuildOptions = {
entryPoints: ["src/chatGpt.ts"],
minify: true,
bundle: true,
outfile: "dist/chatGpt.js",
target: "es2020",
plugins: [denoPlugin(), GasPlugin],
};
Expand All @@ -16,7 +25,10 @@ await Deno.mkdir("dist", { recursive: true });
// Copy appsscript.json
await Deno.copyFile("src/appsscript.json", "dist/appsscript.json");
// Build TypeScript files
await build(buildOptions).catch((err: Error) => {
await build(gptBuildOptions).catch((err: Error) => {
console.error(err);
});
await build(chatGptBuildOptions).catch((err: Error) => {
console.error(err);
});

Expand Down
343 changes: 272 additions & 71 deletions deno.lock

Large diffs are not rendered by default.

22 changes: 22 additions & 0 deletions src/chatGpt.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { createCompletionByChatGpt } from "./openai/index.ts";
import type { ChatGptFunction } from "./types/openai.ts";

/**
* main function
*/
declare const global: {
[x: string]: ChatGptFunction;
};

function CHATGPT(
prompt: string,
system?: string,
): string {
const response = createCompletionByChatGpt(
prompt,
system,
);
return response;
}

global.CHATGPT = CHATGPT;
2 changes: 1 addition & 1 deletion src/main.ts → src/gpt.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createCompletionByGpt3 } from "./openai/index.ts";
import { GptFunction, GptRequestOptions } from "./types/openai.ts";
import type { GptFunction, GptRequestOptions } from "./types/openai.ts";

/**
* main function
Expand Down
42 changes: 42 additions & 0 deletions src/openai/chatgpt/createCompletionByChatGpt.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import type { ChatGptApiInfo } from "../../types/openai.ts";
import {
ChatGptSystemSchema,
} from "../../schemas/openaiSchema.ts";
import { httpRequestWithRetriesForChatGpt } from "./httpRequestWithRetriesForChatGpt.ts";
import { getPropertiesService } from "../../utils/getPropertiesService.ts";

/**
* Create Text Completion with OpenAI ChatGPT
*
* @param {string} prompt Prompt
* @param {string} system System Role
* @param {number} maxTokens Max Tokens
* @param {number} temperature Temperature
* @return Response text returned by ChatGPT
*/
const createCompletionByChatGpt = (
prompt: string,
system?: string,
) => {
if (!prompt) {
throw new Error("You have to input the prompt at the least.");
}

if (system) {
ChatGptSystemSchema.parse(system);
}

const OPENAI_API_KEY: string = getPropertiesService("OPENAI_API_KEY");
const response = httpRequestWithRetriesForChatGpt(
OPENAI_API_KEY,
prompt,
system,
);
if (!response) {
throw new Error("Error: Response error.");
}
const parsedRes = JSON.parse(response.getContentText()) as ChatGptApiInfo;
return parsedRes.choices[0].message.content.trim();
};

export { createCompletionByChatGpt };
61 changes: 61 additions & 0 deletions src/openai/chatgpt/httpRequestWithRetriesForChatGpt.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { sleepForBackoff } from "../../utils/sleepForBackoff.ts";

/**
* Execute HTTP requests and retry failed requests.
* @param {string} OPENAI_API_KEY OpenAI API key
* @param {string} prompt Prompt
* @return Response returned by ChatGPT
*/

const httpRequestWithRetriesForChatGpt = (
OPENAI_API_KEY: string,
prompt: string,
system: string | undefined,
) => {
const url = "https://api.openai.com/v1/chat/completions";
const messageArray = [
{
role: "user",
content: prompt,
},
];

if (system) {
const systemItem = {
role: "system",
content: system,
};
messageArray.unshift(systemItem);
}

const payload = {
model: "gpt-3.5-turbo",
messages: messageArray,
};

const fetchOptions = {
contentType: "application/json",
headers: { Authorization: "Bearer " + OPENAI_API_KEY },
muteHttpExceptions: true,
payload: JSON.stringify(payload),
};

let response = null;
for (let numRetries = 0; numRetries < 5; numRetries++) {
const lastRequestTime = Date.now();
try {
Logger.log(`Sending HTTP request to ${url}`);
response = UrlFetchApp.fetch(url, fetchOptions);
const responseCode = response.getResponseCode();
if (responseCode !== 429 && responseCode < 500) {
return response;
}
} catch (error) {
throw new Error(`Error: ${error}`);
}
Logger.log(`Retrying after ${numRetries} failed requests.`);
sleepForBackoff(numRetries, lastRequestTime);
}
return response;
};
export { httpRequestWithRetriesForChatGpt };
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import type { GptApiInfo, GptRequestOptions } from "../types/openai.ts";
import type { GptApiInfo, GptRequestOptions } from "../../types/openai.ts";
import {
GptMaxTokensSchema,
GptModelSchema,
GptTemperatureSchema,
} from "../schemas/openaiSchema.ts";
import { httpRequestWithRetries } from "./httpRequestWithRetries.ts";
import { getPropertiesService } from "../utils/getPropertiesService.ts";
} from "../../schemas/openaiSchema.ts";
import { httpRequestWithRetriesForGpt3 } from "./httpRequestWithRetriesForGpt3.ts";
import { getPropertiesService } from "../../utils/getPropertiesService.ts";

/**
* Create Text Completion with OpenAI GPT-3
Expand Down Expand Up @@ -39,7 +39,7 @@ const createCompletionByGpt3 = (
}

const OPENAI_API_KEY: string = getPropertiesService("OPENAI_API_KEY");
const response = httpRequestWithRetries(
const response = httpRequestWithRetriesForGpt3(
OPENAI_API_KEY,
prompt,
model,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { sleepForBackoff } from "../utils/sleepForBackoff.ts";
import { sleepForBackoff } from "../../utils/sleepForBackoff.ts";

/**
* Execute HTTP requests and retry failed requests.
Expand All @@ -10,7 +10,7 @@ import { sleepForBackoff } from "../utils/sleepForBackoff.ts";
* @return Response returned by GPT-3
*/

const httpRequestWithRetries = (
const httpRequestWithRetriesForGpt3 = (
OPENAI_API_KEY: string,
prompt: string | string[],
model: string,
Expand Down Expand Up @@ -52,4 +52,4 @@ const httpRequestWithRetries = (
}
return response;
};
export { httpRequestWithRetries };
export { httpRequestWithRetriesForGpt3 };
6 changes: 3 additions & 3 deletions src/openai/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createCompletionByGpt3 } from "./createCompletionByGpt3.ts";
import { httpRequestWithRetries } from "./httpRequestWithRetries.ts";
import { createCompletionByGpt3 } from "./gpt/createCompletionByGpt3.ts";
import { createCompletionByChatGpt } from "./chatgpt/createCompletionByChatGpt.ts";

export { createCompletionByGpt3, httpRequestWithRetries };
export { createCompletionByChatGpt, createCompletionByGpt3 };
23 changes: 23 additions & 0 deletions src/schemas/openaiSchema.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
import { z } from "https://deno.land/x/zod@v3.20.2/mod.ts";

const ChatGptModelSchema = z.literal("gpt-3.5-turbo");

const ChatGptRoleSchema = z.enum([
"system",
"user",
"assistant",
]);

const GptModelSchema = z.enum([
"text-davinci-003",
"text-curie-001",
"text-babbage-001",
"text-ada-001",
]).optional();

const ChatGptSystemSchema = z.string().optional();
const ChatGptPromptSchema = z.string();

const GptPromptSchema = z.string().or(z.string().array());
const GptMaxTokensSchema = z.number().optional();
const GptTemperatureSchema = z.number().optional();
Expand All @@ -16,9 +28,20 @@ const GptFunctionArgsSchema = z.tuple([
GptModelSchema,
GptTemperatureSchema,
]);

const ChatGptFunctionArgsSchema = z.tuple([
ChatGptPromptSchema,
ChatGptSystemSchema,
]);

const ChatGptFunctionSchema = z.function(ChatGptFunctionArgsSchema, z.string());
const GptFunctionSchema = z.function(GptFunctionArgsSchema, z.string());

export {
ChatGptSystemSchema,
ChatGptFunctionSchema,
ChatGptModelSchema,
ChatGptRoleSchema,
GptFunctionSchema,
GptMaxTokensSchema,
GptModelSchema,
Expand Down
50 changes: 48 additions & 2 deletions src/types/openai.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,25 @@
import { z } from "https://deno.land/x/zod@v3.20.2/mod.ts";
import { GptFunctionSchema, GptModelSchema } from "../schemas/openaiSchema.ts";
import {
ChatGptFunctionSchema,
ChatGptModelSchema,
ChatGptRoleSchema,
GptFunctionSchema,
GptModelSchema,
} from "../schemas/openaiSchema.ts";

type ChatGptModel = z.infer<typeof ChatGptModelSchema>;
type GptModel = z.infer<typeof GptModelSchema>;

type MessageItem = {
role: z.infer<typeof ChatGptRoleSchema>;
content: string;
};

type ChatGptRequestOptions = {
model: ChatGptModel;
messages: MessageItem[];
};

type GptRequestOptions = {
model: GptModel;
prompt: string | string[];
Expand All @@ -22,8 +39,28 @@ type GptRequestOptions = {
user?: string;
};

type ChatGptFunction = z.infer<typeof ChatGptFunctionSchema>;
type GptFunction = z.infer<typeof GptFunctionSchema>;

type ChatGptApiInfo = {
id: string;
object: string;
created: string;
model: string;
usage: {
prompt_tokens: number;
completion_tokens: number;
total_tokens: number;
};
choices: [
{
message: MessageItem;
},
];
finish_reason: string;
index: number;
};

type GptApiInfo = {
id: string;
object: string;
Expand All @@ -43,4 +80,13 @@ type GptApiInfo = {
};
};

export type { GptApiInfo, GptFunction, GptModel, GptRequestOptions };
export type {
ChatGptApiInfo,
ChatGptFunction,
ChatGptModel,
ChatGptRequestOptions,
GptApiInfo,
GptFunction,
GptModel,
GptRequestOptions,
};

0 comments on commit ac8dcc3

Please sign in to comment.