新增:ChatGPT 和 Whisper API 已经添加到库中,可以直接使用。
通过给它星标⭐,帮助它成长,并让需要的人发现它。
一个开源的客户端包,允许开发者轻松将 OpenAI 的最先进 AI 模型集成到他们的 Dart/Flutter 应用中。
该库提供了简单直观的方法来向 OpenAI 的各种 API 发出请求,包括 GPT-3 语言模型、DALL-E 图像生成等。
该包设计轻量级且易于使用,因此你可以专注于构建应用程序,而无需担心处理 HTTP 请求带来的复杂性和错误。
非官方
OpenAI 没有任何官方的 Dart 库。
感谢:
感谢本项目的贡献者和赞助商,使它得以存在并维持至今:
也请考虑帮助这个项目。
✨ 主要功能
- 使用简单的方法,完全符合 OpenAI 文档,还提供了额外的功能,使其更适合 Dart 编程语言。
- 只需授权一次,在应用中的任何地方和任何时间使用。
- 对开发者友好。
Stream
功能用于补全 API 和微调事件 API。- 在
/example
文件夹中提供几乎所有实现的现成示例/代码片段。
👑 代码进度(100%)
💫 测试进度(100%)
📜 完整文档:
关于该库提供的所有成员的完整文档,请查看这里。
🟢 使用方法
认证
API 密钥
OpenAI API 使用 API 密钥进行认证。你可以访问你的账户的 API 密钥 来获取账户的 API 密钥。
强烈建议在运行时从 .env
文件加载你的密钥,你可以使用 envied 包或任何其他相同功能的包。
// .env
OPEN_AI_API_KEY=<替换为你的API密钥>
// lib/env/env.dart
import 'package:envied/envied.dart';
part 'env.g.dart';
@Envied(path: ".env")
abstract class Env {
@EnviedField(varName: 'OPEN_AI_API_KEY') // .env 变量。
static const apiKey = _Env.apiKey;
}
// lib/main.dart
void main() {
OpenAI.apiKey = Env.apiKey; // 使用该API密钥初始化包,所有方法现在都可以使用。
// ..
}
如果没有设置 apiKey
,并且你尝试访问 OpenAI.instance
,在实际请求之前就会抛出 MissingApiKeyException
。
如果 apiKey
已设置,但在进行请求时无效,在你的应用中将抛出 RequestFailedException
,有关更多信息,请查看错误处理部分。
设置组织
如果你属于特定的组织,你可以将其 ID 传递给 OpenAI.organization
,如下所示:
OpenAI.organization = "组织ID";
如果你实际上不属于任何组织,你可以忽略这一部分,或者将其设置为 null
。
设置默认请求超时时间
该包使用 http 进行请求,默认超时时间为 30 秒,这意味着任何超过 30 秒的请求将被取消,并抛出异常。要更改此默认值,你需要设置自己的默认超时:
OpenAI.requestsTimeOut = Duration(seconds: 60); // 60 秒。
现在,耗时的方法将在抛出异常前等待 60 秒来获取响应。
设置自己的基本 URL
你可以将包中使用的基本 URL 更改为自己的 URL,如果你想代理请求到 OpenAI API,或者想使用自己的服务器作为 OpenAI API 的代理,这将非常有用。
OpenAI.baseUrl = "https://api.openai.com/v1"; // 默认的 URL。
启用调试和日志记录
你可以设置 showLogs
以便包记录操作流程和步骤:
OpenAI.showLogs = true;
这只会记录请求的步骤,例如请求何时开始和结束,解码何时开始等……
但如果你想记录 API 返回的原始响应(JSON, RAW...),你可以设置 showResponsesLogs
:
OpenAI.showResponsesLogs = true;
这将记录 API 返回的原始响应,例如请求成功时,或请求失败时。(不包括流式响应)
模型
列出模型
列出当前可用的模型,并提供有关每个模型的信息,例如所有者和可用性。
List<OpenAIModelModel> models = await OpenAI.instance.model.list();
OpenAIModelModel firstModel = models.first;
print(firstModel.id); // ...
print(firstModel.permission); // ...
检索模型
检索单个模型的 ID 并获取有关它的其他信息。
OpenAIModelModel model = await OpenAI.instance.model.retrieve("text-davinci-003");
print(model.ownedBy); // ...
如果您提供的模型 ID 不存在或对您的帐户不可用,将抛出 RequestFailedException
,请检查 错误处理 部分。
删除微调模型
但是,如果您想删除一个微调后的模型,您可以使用 delete()
方法:
bool isDeleted = await OpenAI.instance.model.delete("fine-tune-id");
print(isDeleted); // ...
完成
创建完成
基于提供的 model
、prompt
和其他异步属性创建一个预测的完成。
OpenAICompletionModel completion = await OpenAI.instance.completion.create(
model: "text-davinci-003",
prompt: "Dart 是一个程序",
maxTokens: 20,
temperature: 0.5,
n: 1,
stop: ["\n"],
echo: true,
seed: 42,
bestOf: 2,
);
print(completion.choices.first.text); // ...
print(completion.systemFingerprint); // ...
print(completion.id); // ...
如果请求失败(例如,如果您传递了无效的 model
),将抛出 RequestFailedException
,请检查 错误处理 部分。
创建完成流
除了调用 OpenAI.instance.completion.create()
的 Future
(异步)方法直到生成完整的完成才会返回实际值外,您还可以获取Stream
,它们会在生成时逐个返回:
Stream<OpenAIStreamCompletionModel> completionStream = OpenAI.instance.completion.createStream(
model: "text-davinci-003",
prompt: "Github 是",
maxTokens: 100,
temperature: 0.5,
topP: 1,
seed: 42,
stop: '###',
n: 2,
);
completionStream.listen((event) {
final firstCompletionChoice = event.choices.first;
print(firstCompletionChoice.index); // ...
print(firstCompletionChoice.text); // ...
});
有用:也检查 createStreamText()
方法
聊天 (ChatGPT)
创建聊天完成
从提供的属性创建一个聊天消息的预测完成:
// 将发送到请求的系统消息。
final systemMessage = OpenAIChatCompletionChoiceMessageModel(
content: [
OpenAIChatCompletionChoiceMessageContentItemModel.text(
"将所有给定的信息以 JSON 格式返回。",
),
],
role: OpenAIChatMessageRole.assistant,
);
// 将发送到请求的用户消息。
final userMessage = OpenAIChatCompletionChoiceMessageModel(
content: [
OpenAIChatCompletionChoiceMessageContentItemModel.text(
"你好,我是由 OpenAI 创建的聊天机器人。你今天过得怎么样?",
),
//! 仅对支持图像的模型(如 gpt-4)允许图像 URL 内容。
OpenAIChatCompletionChoiceMessageContentItemModel.imageUrl(
"https://placehold.co/600x400",
),
],
role: OpenAIChatMessageRole.user,
);
// 将发送的所有消息。
final requestMessages = [
systemMessage,
userMessage,
];
// 实际请求。
OpenAIChatCompletionModel chatCompletion = await OpenAI.instance.chat.create(
model: "gpt-3.5-turbo-1106",
responseFormat: {"type": "json_object"},
seed: 6,
messages: requestMessages,
temperature: 0.2,
maxTokens: 500,
);
print(chatCompletion.choices.first.message); // ...
print(chatCompletion.systemFingerprint); // ...
print(chatCompletion.usage.promptTokens); // ...
print(chatCompletion.id); // ...
创建聊天完成流
除了调用 OpenAI.instance.chat.create()
的 Future
(异步)方法直到生成完整的聊天才会解析外,您还可以获取 Stream
,它们会在生成时逐个返回:
// 将发送到请求的用户消息。
final userMessage = OpenAIChatCompletionChoiceMessageModel(
content: [
OpenAIChatCompletionChoiceMessageContentItemModel.text(
"你好朋友!",
),
],
role: OpenAIChatMessageRole.user,
);
// 将发送的请求。
final chatStream = OpenAI.instance.chat.createStream(
model: "gpt-3.5-turbo",
messages: [
userMessage,
],
seed: 423,
n: 2,
);
// 监听流。
chatStream.listen(
(streamChatCompletion) {
final content = streamChatCompletion.choices.first.delta.content;
print(content);
},
onDone: () {
print("完成");
},
);
工具(新实现的函数调用)
聊天 API 提供了 tools
功能,该功能允许从聊天 API 调用函数,这个功能已经在包中实现,可以像下面这样使用,请注意这只是一个展示,您应该在应用中处理边缘情况,例如没有工具调用,或者工具调用不是你发送的,等等:
OpenAI.apiKey = Env.apiKey;
// 工具将调用的函数。
void sumNumbers(int number1, int number2) {
print("您的总和结果是 ${number1 + number2}");
}
// 将发送到 API 的工具对象。
final sumNumbersTool = OpenAIToolModel(
type: "function",
function: OpenAIFunctionModel.withParameters(
name: "sumOfTwoNumbers",
parameters: [
OpenAIFunctionProperty.integer(
name: "number1",
description: "第一个要加的数字",
),
OpenAIFunctionProperty.integer(
name: "number2",
description: "第二个要加的数字",
),
],
),
);
// 将发送到 API 的用户文本消息。
final userMessage = OpenAIChatCompletionChoiceMessageModel(
content: [
OpenAIChatCompletionChoiceMessageContentItemModel.text(
"9996 加 3 的和是多少?",
),
],
role: OpenAIChatMessageRole.user,
);
// 实际调用。
final chat = await OpenAI.instance.chat.create(
model: "gpt-3.5-turbo",
messages: [userMessage],
tools: [sumNumbersTool],
);
// ! 此处理仅用于展示,未完成,因为不会处理您在应用中应该处理的边缘情况。
final message = chat.choices.first.message;
// 是否有工具调用。
if (message.haveToolCalls) {
final call = message.toolCalls!.first;
// 是否是我们发送的工具调用。
if (call.function.name == "sumOfTwoNumbers") {
// 从工具调用中解码参数。
final decodedArgs = jsonDecode(call.function.arguments);
final number1 = decodedArgs["number1"];
final number2 = decodedArgs["number2"];
// 用参数调用函数。
sumNumbers(number1, number2);
}
}
从这里了解更多。
编辑
创建编辑
基于使用的模型创建给定提示的编辑版本。
OpenAIEditModel edit = await OpenAI.instance.edit.create(
model: "text-davinci-edit-001";
instruction: "从输入文本中移除所有 '!'",
input: "你好!!, 我!需要!移除!",
n: 1,
temperature: 0.8,
);
// 打印选择项
for (int index = 0; index < edit.choices.length; index++) {
print(edit.choices[index].text);
}
图像
创建图像
基于给定的提示生成新图像。
OpenAIImageModel image = await OpenAI.instance.image.create(
prompt: '在海上的宇航员',
n: 1,
size: OpenAIImageSize.size1024,
responseFormat: OpenAIImageResponseFormat.url,
);
// 将输出打印到控制台。
for (int index = 0; index < image.data.length; index++) {
final currentItem = image.data[index];
print(currentItem.url);
}
创建图像编辑
基于原始图像和提示创建编辑或扩展的图像。 <SOURCE_TEXT>
OpenAIImageModel imageEdits = await OpenAI.instance.image.edit(
prompt: 'mask the image with color red',
image: File(/* IMAGE PATH HERE */),
mask: File(/* MASK PATH HERE */),
n: 1,
size: OpenAIImageSize.size1024,
responseFormat: OpenAIImageResponseFormat.b64Json,
);
for (int index = 0; index < imageEdits.data.length; index++) {
final currentItem = imageEdits.data[index];
print(currentItem.b64Json);
}
创建图片变体
创建给定图像的变体。
// 创建图片变体
final imageVariations = await OpenAI.instance.image.variation(
model: "dall-e-2",
image: File("dart.png"),
n: 4,
size: OpenAIImageSize.size512,
responseFormat: OpenAIImageResponseFormat.url,
);
// 输出结果到控制台
for (var index = 0; index < imageVariations.data.length; index++) {
final currentItem = imageVariations.data[index];
print(currentItem.url);
}
嵌入
获取给定输入的向量表示,这些向量可以被机器学习模型和算法方便地使用。
创建嵌入
final embedding = await OpenAI.instance.embedding.create(
model: "text-embedding-ada-002",
input: "This is a sample text",
);
for (int index = 0; index < embedding.data.length; index++) {
final currentItem = embedding.data[index];
print(currentItem);
}
音频
创建语音
要从文本创建语音,你可以使用createSpeech()
方法直接提供所需参数:
// 语音请求
File speechFile = await OpenAI.instance.audio.createSpeech(
model: "tts-1",
input: "Say my name is Anas",
voice: "nova",
responseFormat: OpenAIAudioSpeechResponseFormat.mp3,
outputDirectory: await Directory("speechOutput").create(),
outputFileName: "anas",
);
// 文件结果
print(speechFile.path);
注意:outputDirectory
和 outputFileName
是此方法的辅助参数,你可以使用它们将音频文件保存到指定目录下,并使用指定名称,文件扩展名将从 responseFormat
中提取。如果你不想使用它们,只需忽略它,音频文件将保存到你的应用程序的默认目录,文件名为 output
。
上面的示例代码将在你的项目的speechOutput
目录中生成anas.mp3
。
创建转录
要转录一个音频文件,你可以直接提供file
属性使用createTranscription()
方法:
OpenAIAudioModel transcription = OpenAI.instance.audio.createTranscription(
file: File(/* 文件路径 */),
model: "whisper-1",
responseFormat: OpenAIAudioResponseFormat.json,
);
// 打印转录内容
print(transcription.text);
创建翻译
要访问翻译 API 并翻译音频文件到英语,你可以提供file
属性使用createTranslation()
方法:
OpenAIAudioModel translation = await OpenAI.instance.audio.createTranslation(
file: File(/* 文件路径 */),
model: "whisper-1",
responseFormat: OpenAIAudioResponseFormat.text,
);
// 打印翻译内容
print(translation.text);
了解更多从这里。
文件
文件被用于上传可以与某些功能(如微调)一起使用的文档。
列出文件
获取你所有上传文件的列表。
List<OpenAIFileModel> files = await OpenAI.instance.file.list();
print(files.first.fileName); // ...
print(files.first.id); // ...
上传文件
上传一个包含文档的文件,可以用在各种端点/功能上。目前,单个组织上传的所有文件的大小可达1 GB。如需增加存储限制,请联系我们。
OpenAIFileModel uploadedFile = await OpenAI.instance.file.upload(
file: File("/* 文件路径 */"),
purpose: "fine-tuning",
);
print(uploadedFile.id); // ...
删除文件
通过文件 ID 删除一个现有文件。
bool isFileDeleted = await OpenAI.instance.file.delete("/* 文件ID */");
print(isFileDeleted);
检索文件
通过文件 ID 获取并返回文件的相关信息。
OpenAIFileModel file = await OpenAI.instance.file.retrieve("文件ID");
print(file.id);
检索文件内容
通过文件 ID 获取单个文件的内容。
dynamic fileContent = await OpenAI.instance.file.retrieveContent("文件ID");
print(fileContent);
微调
创建微调
根据给定的数据集创建一个微调模型任务,并返回关于排队任务的微调对象。
OpenAIFineTuneModel fineTune = await OpenAI.instance.fineTune.create(
trainingFile: "文件ID",
);
print(fineTune.status); // ...
列出微调
列出你组织的微调任务。
List<OpenAIFineTuneModel> fineTunes = await OpenAI.instance.fineTune.list();
print(fineTunes.first); // ...
检索微调
通过微调 ID 检索一个微调任务。
OpenAIFineTuneModel fineTune = await OpenAI.instance.fineTune.retrieve("微调ID");
print(fineTune.id); // ...
取消微调
通过微调 ID 取消一个微调任务,并返回相应结果。
OpenAIFineTuneModel cancelledFineTune = await OpenAI.instance.fineTune.cancel("微调ID");
print(cancelledFineTune.status); // ...
列出微调事件
通过微调 ID 列出单个微调任务的进度事件。
List<OpenAIFineTuneEventModel> events = await OpenAI.instance.fineTune.listEvents("微调ID");
print(events.first.message); // ...
监听微调事件 Stream
按微调 ID 流式获取所有微调任务的事件信息。
这个操作将是长时间运行的,直到微调任务终止才会返回结果。
每当有新事件可用时,流将会发出一个事件。
Stream<OpenAIFineTuneEventStreamModel> eventsStream = OpenAI.instance.fineTune.listEventsStream("微调ID");
eventsStream.listen((event) {
print(event.message);
});
删除微调
按微调 ID 删除一个微调任务。
bool deleted = await OpenAI.instance.fineTune.delete("微调ID");
print(deleted); // ...
审核
创建审核
分类文本是否违反 OpenAI 的内容政策
OpenAIModerationModel moderation = await OpenAI.instance.moderation.create(
input: "I want to kill him",
);
print(moderation.results); // ...
print(moderation.results.first.categories.hate); // ...
错误处理
任何时候当 OpenAI API 发生错误时(例如:当你尝试从非图像文件创建图像变体...),RequestFailedException
将会在你的 Flutter / Dart 应用程序中自动抛出,你可以使用 try-catch
捕捉该错误,并基于错误采取相应行动:
try {
// 这将抛出一个错误。
final errorVariation = await OpenAI.instance.image.variation(
image: File(/* 非图像文件路径 */),
);
} on RequestFailedException catch(e) {
print(e.message);
print(e.statusCode);
}
想要帮助吗?
请记住,任何与这些任务相关的帮助都是欢迎的,这是为了社区的利益。 </SOURCE_TEXT>
- 撰写文档:如果你看到任何未记录的类、属性、方法,并且你知道它们的功能,请花2分钟的时间帮助其他开发者。
- 代码重构:我知道这是我的工作而不是你的,但如果你能并且愿意,你非常欢迎参与。
- 代码审查:如果你发现有更好的方法在SDK中实现某个功能,请告知我。
- 如果你尝试过任何用例的示例,或者你自己有示例并想将其包含在examples/中,请继续。
- 如果API、Dart、某个包甚至与此包相关的Flutter中存在任何更新,请提及。
- 捐赠给项目,这将帮助我继续工作并使其变得更好。