Skip to content

Commit

Permalink
feat: Add support for different content types in Assistants API and o…
Browse files Browse the repository at this point in the history
…ther fixes (#412)
  • Loading branch information
davidmigloz committed May 11, 2024
1 parent 326212c commit 97acab4
Show file tree
Hide file tree
Showing 26 changed files with 2,714 additions and 820 deletions.
30 changes: 29 additions & 1 deletion packages/openai_dart/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -743,7 +743,35 @@ final res = await client.createThreadMessage(
threadId: threadId,
request: CreateMessageRequest(
role: CreateMessageRequestRole.user,
content: 'I need to solve the equation `3x + 11 = 14`. Can you help me?',
content: CreateMessageRequestContent.text(
'I need to solve the equation `3x + 11 = 14`. Can you help me?',
),
),
);
```

If you need to send multi-modal content, you can use the `CreateMessageRequestContent.parts([...])` method:

```dart
final res = await client.createThreadMessage(
threadId: threadId,
request: CreateMessageRequest(
role: CreateMessageRequestRole.user,
content: CreateMessageRequestContent.parts([
MessageContent.text(
text: MessageContentText(
value: 'Some text...',
),
),
MessageContent.imageFile(
imageFile: MessageContentImageFile(fileId: 'file-abc123'),
),
MessageContent.imageUrl(
imageUrl: MessageContentImageUrl(
url: 'https://example.com/image.jpg',
),
),
]),
),
);
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ class CreateMessageRequest with _$CreateMessageRequest {
required MessageRole role,

/// The content of the message.
required String content,
@_CreateMessageRequestContentConverter()
required CreateMessageRequestContent content,

/// A list of files attached to the message, and the tools they were added to.
@JsonKey(includeIfNull: false) List<MessageAttachment>? attachments,
Expand All @@ -40,18 +41,8 @@ class CreateMessageRequest with _$CreateMessageRequest {
'metadata'
];

/// Validation constants
static const contentMinLengthValue = 1;
static const contentMaxLengthValue = 256000;

/// Perform validations on the schema property values
String? validateSchema() {
if (content.length < contentMinLengthValue) {
return "The value of 'content' cannot be < $contentMinLengthValue characters";
}
if (content.length > contentMaxLengthValue) {
return "The length of 'content' cannot be > $contentMaxLengthValue characters";
}
return null;
}

Expand All @@ -65,3 +56,54 @@ class CreateMessageRequest with _$CreateMessageRequest {
};
}
}

// ==========================================
// CLASS: CreateMessageRequestContent
// ==========================================

/// The content of the message.
@freezed
sealed class CreateMessageRequestContent with _$CreateMessageRequestContent {
const CreateMessageRequestContent._();

/// An array of content parts with a defined type, each can be of type `text` or images can be passed with `image_url` or `image_file`. Image types are only supported on [Vision-compatible models](https://platform.openai.com/docs/models/overview).
const factory CreateMessageRequestContent.parts(
List<MessageContent> value,
) = CreateMessageRequestContentListMessageContent;

/// The text contents of the message.
const factory CreateMessageRequestContent.text(
String value,
) = CreateMessageRequestContentString;

/// Object construction from a JSON representation
factory CreateMessageRequestContent.fromJson(Map<String, dynamic> json) =>
_$CreateMessageRequestContentFromJson(json);
}

/// Custom JSON converter for [CreateMessageRequestContent]
class _CreateMessageRequestContentConverter
implements JsonConverter<CreateMessageRequestContent, Object?> {
const _CreateMessageRequestContentConverter();

@override
CreateMessageRequestContent fromJson(Object? data) {
if (data is List && data.every((item) => item is MessageContent)) {
return CreateMessageRequestContentListMessageContent(data.cast());
}
if (data is String) {
return CreateMessageRequestContentString(data);
}
throw Exception(
'Unexpected value for CreateMessageRequestContent: $data',
);
}

@override
Object? toJson(CreateMessageRequestContent data) {
return switch (data) {
CreateMessageRequestContentListMessageContent(value: final v) => v,
CreateMessageRequestContentString(value: final v) => v,
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class CreateVectorStoreRequest with _$CreateVectorStoreRequest {
@JsonKey(name: 'file_ids', includeIfNull: false) List<String>? fileIds,

/// The name of the vector store.
@JsonKey(includeIfNull: false) String? name,
required String name,

/// The expiration policy for a vector store.
@JsonKey(name: 'expires_after', includeIfNull: false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class DeleteVectorStoreFileResponse with _$DeleteVectorStoreFileResponse {
required bool deleted,

/// The object type, which is always `vector_store.file.deleted`.
required DeleteVectorStoreFileResponseObject object,
required String object,
}) = _DeleteVectorStoreFileResponse;

/// Object construction from a JSON representation
Expand All @@ -46,13 +46,3 @@ class DeleteVectorStoreFileResponse with _$DeleteVectorStoreFileResponse {
};
}
}

// ==========================================
// ENUM: DeleteVectorStoreFileResponseObject
// ==========================================

/// The object type, which is always `vector_store.file.deleted`.
enum DeleteVectorStoreFileResponseObject {
@JsonValue('vector_store.file.deleted')
vectorStoreFileDeleted,
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class DeleteVectorStoreResponse with _$DeleteVectorStoreResponse {
required bool deleted,

/// The object type, which is always `vector_store.deleted`.
required DeleteVectorStoreResponseObject object,
required String object,
}) = _DeleteVectorStoreResponse;

/// Object construction from a JSON representation
Expand All @@ -46,13 +46,3 @@ class DeleteVectorStoreResponse with _$DeleteVectorStoreResponse {
};
}
}

// ==========================================
// ENUM: DeleteVectorStoreResponseObject
// ==========================================

/// The object type, which is always `vector_store.deleted`.
enum DeleteVectorStoreResponseObject {
@JsonValue('vector_store.deleted')
vectorStoreDeleted,
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ class ListVectorStoresResponse with _$ListVectorStoresResponse {
required List<VectorStoreObject> data,

/// The ID of the first assistant file in the list.
@JsonKey(name: 'first_id') required String firstId,
@JsonKey(name: 'first_id') required String? firstId,

/// The ID of the last assistant file in the list.
@JsonKey(name: 'last_id') required String lastId,
@JsonKey(name: 'last_id') required String? lastId,

/// Whether there are more assistant files available.
@JsonKey(name: 'has_more') required bool hasMore,
Expand Down
30 changes: 28 additions & 2 deletions packages/openai_dart/lib/src/generated/schema/message_content.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,33 @@ sealed class MessageContent with _$MessageContent {
/// References an image [File](https://platform.openai.com/docs/api-reference/files) in the content of a message.
const factory MessageContent.imageFile({
/// Always `image_file`.
required String type,
@Default('image_file') String type,

/// The image file that is part of a message.
@JsonKey(name: 'image_file') required MessageContentImageFile imageFile,
}) = MessageContentImageFileObject;

// ------------------------------------------
// UNION: MessageContentImageUrlObject
// ------------------------------------------

/// References an image URL in the content of a message.
const factory MessageContent.imageUrl({
/// The type of the content part. Always `image_url`.
@Default('image_url') String type,

/// The image URL part of a message.
@JsonKey(name: 'image_url') required MessageContentImageUrl imageUrl,
}) = MessageContentImageUrlObject;

// ------------------------------------------
// UNION: MessageContentTextObject
// ------------------------------------------

/// The text content that is part of a message.
const factory MessageContent.text({
/// Always `text`.
required String type,
@Default('text') String type,

/// The text content that is part of a message.
required MessageContentText text,
Expand All @@ -43,3 +56,16 @@ sealed class MessageContent with _$MessageContent {
factory MessageContent.fromJson(Map<String, dynamic> json) =>
_$MessageContentFromJson(json);
}

// ==========================================
// ENUM: MessageContentEnumType
// ==========================================

enum MessageContentEnumType {
@JsonValue('image_file')
imageFile,
@JsonValue('image_url')
imageUrl,
@JsonValue('text')
text,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: invalid_annotation_target
part of open_a_i_schema;

// ==========================================
// ENUM: MessageContentImageDetail
// ==========================================

/// Specifies the detail level of the image if specified by the user. `low` uses fewer tokens, you can opt in to high resolution using `high`.
enum MessageContentImageDetail {
@JsonValue('auto')
auto,
@JsonValue('low')
low,
@JsonValue('high')
high,
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,19 @@ class MessageContentImageFile with _$MessageContentImageFile {

/// Factory constructor for MessageContentImageFile
const factory MessageContentImageFile({
/// The [File](https://platform.openai.com/docs/api-reference/files) ID of the image in the message content.
/// The [File](https://platform.openai.com/docs/api-reference/files) ID of the image in the message content. Set `purpose="vision"` when uploading the File if you need to later display the file content.
@JsonKey(name: 'file_id') required String fileId,

/// Specifies the detail level of the image if specified by the user. `low` uses fewer tokens, you can opt in to high resolution using `high`.
@Default(MessageContentImageDetail.auto) MessageContentImageDetail detail,
}) = _MessageContentImageFile;

/// Object construction from a JSON representation
factory MessageContentImageFile.fromJson(Map<String, dynamic> json) =>
_$MessageContentImageFileFromJson(json);

/// List of all property names of schema
static const List<String> propertyNames = ['file_id'];
static const List<String> propertyNames = ['file_id', 'detail'];

/// Perform validations on the schema property values
String? validateSchema() {
Expand All @@ -35,6 +38,7 @@ class MessageContentImageFile with _$MessageContentImageFile {
Map<String, dynamic> toMap() {
return {
'file_id': fileId,
'detail': detail,
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: invalid_annotation_target
part of open_a_i_schema;

// ==========================================
// CLASS: MessageContentImageUrl
// ==========================================

/// The image URL part of a message.
@freezed
class MessageContentImageUrl with _$MessageContentImageUrl {
const MessageContentImageUrl._();

/// Factory constructor for MessageContentImageUrl
const factory MessageContentImageUrl({
/// The external URL of the image, must be a supported image types: jpeg, jpg, png, gif, webp.
required String url,

/// Specifies the detail level of the image if specified by the user. `low` uses fewer tokens, you can opt in to high resolution using `high`.
@Default(MessageContentImageDetail.auto) MessageContentImageDetail detail,
}) = _MessageContentImageUrl;

/// Object construction from a JSON representation
factory MessageContentImageUrl.fromJson(Map<String, dynamic> json) =>
_$MessageContentImageUrlFromJson(json);

/// List of all property names of schema
static const List<String> propertyNames = ['url', 'detail'];

/// Perform validations on the schema property values
String? validateSchema() {
return null;
}

/// Map representation of object (not serialized)
Map<String, dynamic> toMap() {
return {
'url': url,
'detail': detail,
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ class MessageContentText with _$MessageContentText {
required String value,

/// A list of annotations that point to specific quotes from specific files.
required List<MessageContentTextAnnotations> annotations,
@JsonKey(includeIfNull: false)
List<MessageContentTextAnnotations>? annotations,
}) = _MessageContentText;

/// Object construction from a JSON representation
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: invalid_annotation_target
part of open_a_i_schema;

// ==========================================
// CLASS: MessageDeltaContentImageUrlObject
// ==========================================

/// References an image URL in the content of a message.
@freezed
class MessageDeltaContentImageUrlObject
with _$MessageDeltaContentImageUrlObject {
const MessageDeltaContentImageUrlObject._();

/// Factory constructor for MessageDeltaContentImageUrlObject
const factory MessageDeltaContentImageUrlObject({
/// The index of the content part in the message.
@JsonKey(includeIfNull: false) int? index,

/// Always `image_url`.
@JsonKey(includeIfNull: false) String? type,

/// The image URL part of a message.
@JsonKey(name: 'image_url', includeIfNull: false)
MessageContentImageUrl? imageUrl,
}) = _MessageDeltaContentImageUrlObject;

/// Object construction from a JSON representation
factory MessageDeltaContentImageUrlObject.fromJson(
Map<String, dynamic> json) =>
_$MessageDeltaContentImageUrlObjectFromJson(json);

/// List of all property names of schema
static const List<String> propertyNames = ['index', 'type', 'image_url'];

/// Perform validations on the schema property values
String? validateSchema() {
return null;
}

/// Map representation of object (not serialized)
Map<String, dynamic> toMap() {
return {
'index': index,
'type': type,
'image_url': imageUrl,
};
}
}
Loading

0 comments on commit 97acab4

Please sign in to comment.