TypeScript 基本用法
TypeChat 目前是一个小型库,所以让我们看看一些基本用法来理解它。
import fs from "fs";
import path from "path";
import { createJsonTranslator, createLanguageModel } from "typechat";
import { processRequests } from "typechat/interactive";
import { createTypeScriptJsonValidator } from "typechat/ts";
import { SentimentResponse } from "./sentimentSchema";
// Create a model.
const model = createLanguageModel(process.env);
// Create a validator.
const schema = fs.readFileSync(path.join(__dirname, "sentimentSchema.ts"), "utf8");
const validator = createTypeScriptJsonValidator<SentimentResponse>(schema, "SentimentResponse");
// Create a translator.
const translator = createJsonTranslator(model, validator);
// Process requests interactively or from the input file specified on the command line
processRequests("😀> ", process.argv[2], async (request) => {
const response = await translator.translate(request);
if (!response.success) {
console.log(response.message);
return;
}
console.log(`The sentiment is ${response.data.sentiment}`);
});
提供模型
TypeChat 可以与任何语言模型一起使用。只要你能构造一个具有以下属性的对象
export interface TypeChatLanguageModel {
/**
* Optional property that specifies the maximum number of retry attempts (the default is 3).
*/
retryMaxAttempts?: number;
/**
* Optional property that specifies the delay before retrying in milliseconds (the default is 1000ms).
*/
retryPauseMs?: number;
/**
* Obtains a completion from the language model for the given prompt.
* @param prompt The prompt string.
*/
complete(prompt: string): Promise<Result<string>>;
}
那么你就可以尝试使用这样的模型来使用 TypeChat。
这里的关键是只需要 complete
。complete
只是一个函数,它接受一个 string
并在一切顺利的情况下最终返回一个 string
。
为方便起见,TypeChat 提供了两个开箱即用的函数来连接 OpenAI API 和 Azure 的 OpenAI 服务。你可以直接调用它们。
export function createOpenAILanguageModel(apiKey: string, model: string, endPoint? string): TypeChatLanguageModel;
export function createAzureOpenAILanguageModel(apiKey: string, endPoint: string): TypeChatLanguageModel;
为了更方便,TypeChat 还提供了一个函数来推断你使用的是 OpenAI 还是 Azure OpenAI。
export function createLanguageModel(env: Record<string, string | undefined>): TypeChatLanguageModel
你可以填充你的环境变量,根据是设置了 OPENAI_API_KEY
还是 AZURE_OPENAI_API_KEY
,你将获得相应类型的模型。
import dotenv from "dotenv";
dotenv.config(/*...*/);
import * as typechat from "typechat";
const model = typechat.createLanguageModel(process.env);
无论你决定如何构建模型,我们都建议将你的秘密令牌/API 密钥保存在 .env
文件中,并在 .gitignore
中指定 .env
。你可以使用像 dotenv
这样的库来帮助加载这些。
加载模式
TypeChat 向语言模型描述类型,以帮助指导它们的响应。在这种情况下,我们使用的是 TypeScriptJsonValidator
,它使用 TypeScript 编译器根据一组类型验证数据。这意味着我们将把我们期望返回的数据类型写入 .ts
文件。以下是我们的模式文件 sentimentSchema.ts
的样子
// The following is a schema definition for determining the sentiment of a some user input.
export interface SentimentResponse {
sentiment: "negative" | "neutral" | "positive"; // The sentiment of the text
}
这也意味着我们需要手动原样加载一个输入 .ts
文件。
// Load up the type from our schema.
import type { SentimentResponse } from "./sentimentSchema";
// Load up the schema file contents.
const schema = fs.readFileSync(path.join(__dirname, "sentimentSchema.ts"), "utf8");
注意:此代码假定为 CommonJS 模块。如果你使用的是 ECMAScript 模块,你可以根据运行时的版本使用 import.meta.url
或通过 import.meta.dirname
。
这给某些类型的构建带来了一些复杂性,因为我们的输入文件需要被视为本地资产。实现这一点的一种方法是使用运行时或工具(如 ts-node
)来导入文件以获取其类型,并读取文件内容。另一种方法是使用像 copyfiles
这样的实用程序将特定的模式文件移动到输出目录。如果你正在使用打包器,也可能存在将文件作为原始字符串导入的自定义方式。无论如何,我们的示例应该适用于前两种选项中的任何一种。
或者,如果需要,我们可以使用 Zod 和 ZodValidator
完全在内存中构建我们的模式,我们稍后会介绍。如果我们走这条路,我们的模式会是这样的。
import { z } from "zod";
export const SentimentResponse = z.object({
sentiment: z.enum(["negative", "neutral", "positive"]).describe("The sentiment of the text")
});
export const SentimentSchema = {
SentimentResponse
};
创建验证器
验证器实际上有两个作用:为语言模型生成文本模式,并确保任何数据都符合给定的形状。接口大致如下
/**
* An object that represents a TypeScript schema for JSON objects.
*/
export interface TypeChatJsonValidator<T extends object> {
/**
* Return a string containing TypeScript source code for the validation schema.
*/
getSchemaText(): string;
/**
* Return the name of the JSON object target type in the schema.
*/
getTypeName(): string;
/**
* Validates the given JSON object according to the associated TypeScript schema. Returns a
* `Success<T>` object containing the JSON object if validation was successful. Otherwise, returns
* an `Error` object with a `message` property describing the error.
* @param jsonText The JSON object to validate.
* @returns The JSON object or an error message.
*/
validate(jsonObject: object): Result<T>;
}
换句话说,这只是所有类型的文本、要响应的顶级类型的名称,以及一个验证函数,如果成功,它将返回输入的强类型视图。
TypeChat 提供了两个验证器。
TypeScriptJsonValidator
TypeScriptJsonValidator
作用于 TypeScript 文本文件。要创建一个,我们必须从 typechat/ts
导入 createTypeScriptJsonValidator
import { createTypeScriptJsonValidator } from "typechat/ts";
我们还需要实际从我们的模式中导入类型。
import { SentimentResponse } from "./sentimentSchema";
有了我们的模式文本和这个类型,我们就有足够的信息来创建一个验证器
const validator = createTypeScriptJsonValidator<SentimentResponse>(schema, "SentimentResponse");
我们提供了模式的文本和我们希望返回数据满足的类型名称。我们还必须提供类型参数 SentimentResponse
来解释我们期望的数据形状(尽管请注意,这有点像类型转换,并且不能保证)。
Zod 验证器
如果你选择使用 Zod 定义模式,可以使用 createZodJsonValidator
函数
import { createZodJsonValidator } from "typechat/zod";
Zod 验证器不需要源文件,而是需要一个 JavaScript 对象,将类型名称映射到 Zod 类型对象,例如以下示例中的 myObj
export const MyType = z.object(/*...*/);
export const MyOtherType = z.object(/*...*/);
export let myObj = {
MyType,
MyOtherType,
}
从上面看,那只是 SentimentSchema
export const SentimentSchema = {
SentimentResponse
};
所以我们需要导入那个对象...
import { SentimentSchema } from "./sentimentSchema";
并将其以及我们预期的类型名称提供给 createZodJsonValidator
const validator = createZodJsonValidator(SentimentSchema, "SentimentResponse");
创建 JSON 转换器
TypeChatJsonTranslator
将这些结合在一起。
import { createJsonTranslator } from "typechat";
转换器既接受模型又接受验证器,并提供一种将一些用户输入转换为我们模式中的对象的方法。为此,它根据模式制作提示,联系模型,解析 JSON 数据,并尝试验证。可选地,如果验证失败,它将制作修复提示并重试。
const translator = createJsonTranslator(model, validator);
当我们准备好转换用户请求时,我们可以调用 translate
方法。
translator.translate("Hello world! 🙂");
我们稍后会回到这里。
创建提示
TypeChat 导出一个 processRequests
函数,可以轻松地进行 TypeChat 实验。我们需要从 typechat/interactive
导入它。
import { processRequests } from "typechat/interactive";
它要么创建一个交互式命令行提示,要么从文件中读取行。
typechat.processRequests("😀> ", process.argv[2], async (request) => {
// ...
});
processRequests
接受 3 个参数。首先是提示前缀——这是用户在交互式场景中在自己的文本之前会看到的内容。你可以让它有趣一些。我们喜欢在这里使用表情符号。😄
接下来,我们接受一个文本文件名。输入字符串将按行从该文件中读取。如果文件名是 undefined
,processRequests
将在标准输入上工作并提供一个交互式提示。使用 process.argv[2]
使我们的程序默认是交互式的,除非运行程序的人提供了输入文件作为命令行参数(例如 node ./dist/main.js inputFile.txt
)。
最后,是请求处理程序。我们将在下一步填写它。
翻译请求
我们的处理程序每次被调用时都会收到一些用户输入(request
字符串)。是时候将该字符串传递给我们的 translator
对象了。
typechat.processRequests("😀> ", process.argv[2], async (request) => {
const response = await translator.translate(request);
if (!response.success) {
console.log(response.message);
return;
}
console.log(`The sentiment is ${response.data.sentiment}`);
});
我们正在对每个字符串调用 translate
方法并获得响应。如果出现问题,TypeChat 将重试请求,最多重试次数由我们 model
上的 retryMaxAttempts
指定。但是,如果初始请求以及所有重试都失败,response.success
将为 false
,我们将能够获取一条 message
解释出了什么问题。
在理想情况下,response.success
将为 true
,我们将能够访问我们类型良好的 data
属性!这将对应于我们在创建转换器对象时传入的类型(即 SentimentResponse
)。
就是这样!你现在应该对 TypeChat 的 API 以及如何开始一个新项目有了基本的了解。🎉