diff --git a/CHANGELOG.md b/CHANGELOG.md index a312964..1502c1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -204,4 +204,14 @@ - Retrieve message - Retrieve message file - Modify message +- Run + - Create a run. + - Create a thread and run it in one request. + - List runs + - List run steps + - Retrieves a run. + - Retrieve run step + - Modifies a run. + - Submit tool outputs to run + - Cancel a run \ No newline at end of file diff --git a/lib/chat_gpt_sdk.dart b/lib/chat_gpt_sdk.dart index 60cb433..27ebd43 100644 --- a/lib/chat_gpt_sdk.dart +++ b/lib/chat_gpt_sdk.dart @@ -56,3 +56,10 @@ export 'src/model/assistant/response/tool.dart'; export 'src/model/thread/request/thread_request.dart'; export 'src/model/thread/response/thread_delete_response.dart'; export 'src/model/thread/response/thread_response.dart'; +export 'src/model/message/request/create_message.dart'; +export 'src/model/message/response/content.dart'; +export 'src/model/message/response/create_message_response.dart'; +export 'src/model/message/response/list_message_file.dart'; +export 'src/model/message/response/list_message_file_data.dart'; +export 'src/model/message/response/message_data.dart'; +export 'src/model/message/response/text.dart'; diff --git a/lib/src/messages.dart b/lib/src/messages.dart index 31797d5..a28472c 100644 --- a/lib/src/messages.dart +++ b/lib/src/messages.dart @@ -1,10 +1,10 @@ -import 'package:chat_gpt_sdk/chat_gpt_sdk.dart'; import 'package:chat_gpt_sdk/src/client/client.dart'; import 'package:chat_gpt_sdk/src/model/message/request/create_message.dart'; import 'package:chat_gpt_sdk/src/model/message/response/create_message_response.dart'; import 'package:chat_gpt_sdk/src/model/message/response/list_message_file.dart'; import 'package:chat_gpt_sdk/src/model/message/response/list_message_file_data.dart'; import 'package:chat_gpt_sdk/src/model/message/response/message_data.dart'; +import 'package:chat_gpt_sdk/src/utils/constants.dart'; class Messages { final OpenAIClient _client; diff --git a/lib/src/model/run/request/create_run.dart b/lib/src/model/run/request/create_run.dart new file mode 100644 index 0000000..f351403 --- /dev/null +++ b/lib/src/model/run/request/create_run.dart @@ -0,0 +1,61 @@ +class CreateRun { + ///The ID of the + ///assistant + /// to use to execute this run. + ///[assistantId] + final String assistantId; + + ///The ID of the + ///Model + /// to be used to execute this run. + /// If a value is provided here, + /// it will override the model associated with the assistant. + /// If not, the model associated with the assistant will be used. + /// [model] + final String? model; + + ///Overrides the + ///instructions + /// of the assistant. + /// This is useful for modifying the behavior on a per-run basis. + /// [instructions] + final String? instructions; + + ///Appends additional instructions at the end of the instructions + /// for the run. This is useful for modifying the behavior + /// on a per-run basis without overriding other instructions. + /// [additionalInstructions] + final String? additionalInstructions; + + ///Override the tools the assistant can use for this run. + /// This is useful for modifying the behavior on a per-run basis. + /// [tools] + final List>? tools; + + ///Set of 16 key-value pairs that can be attached to an object. + /// This can be useful for storing additional information + /// about the object in a structured format. Keys can be a + /// maximum of 64 characters long and values can be a + /// maxium of 512 characters long. + /// [metadata] + final Map? metadata; + + CreateRun({ + required this.assistantId, + this.model, + this.instructions, + this.additionalInstructions, + this.tools, + this.metadata, + }); + + Map toJson() => Map.of({ + 'assistant_id': assistantId, + 'model': model, + 'instructions': instructions, + 'additional_instructions': additionalInstructions, + 'tools': tools, + 'metadata': metadata, + }) + ..removeWhere((_, value) => value == null); +} diff --git a/lib/src/model/run/request/create_thread_and_run.dart b/lib/src/model/run/request/create_thread_and_run.dart new file mode 100644 index 0000000..b748aa7 --- /dev/null +++ b/lib/src/model/run/request/create_thread_and_run.dart @@ -0,0 +1,64 @@ +class CreateThreadAndRun { + ///The ID of the + ///assistant + /// to use to execute this run. + /// [assistantId] + final String assistantId; + + /// + /// [thread] + /// ## Example + /// ```dart + /// { + /// "messages": [ + /// {"role": "user", "content": "Explain deep learning to a 5 year old."} + /// ] + /// } + /// ``` + final Map? thread; + + ///The ID of the + ///Model + /// to be used to execute this run. + /// If a value is provided here, it will override the + /// model associated with the assistant. + /// If not, the model associated with the assistant will be used. + /// [model] + final String? model; + + ///Override the default system message of the assistant. + /// This is useful for modifying the behavior on a per-run basis. + /// [instructions] + final String? instructions; + + ///Override the tools the assistant can use for this run. + /// This is useful for modifying the behavior on a per-run basis. + /// [tools] + final List>? tools; + + ///Set of 16 key-value pairs that can be attached to an object. + /// This can be useful for storing additional information about + /// the object in a structured format. Keys can be a maximum of 64 + /// characters long and values can be a maxium of 512 characters long. + /// [metadata] + final Map? metadata; + + CreateThreadAndRun({ + required this.assistantId, + this.thread, + this.model, + this.instructions, + this.tools, + this.metadata, + }); + + Map toJson() => Map.of({ + 'assistant_id': assistantId, + 'thread': thread, + 'model': model, + 'instructions': instructions, + 'tools': tools, + 'metadata': metadata, + }) + ..removeWhere((_, value) => value == null); +} diff --git a/lib/src/model/run/response/create_run_response.dart b/lib/src/model/run/response/create_run_response.dart new file mode 100644 index 0000000..a9afcc3 --- /dev/null +++ b/lib/src/model/run/response/create_run_response.dart @@ -0,0 +1,102 @@ +import 'package:chat_gpt_sdk/src/model/assistant/response/tool.dart'; +import 'package:chat_gpt_sdk/src/model/run/response/step_detail.dart'; +import 'package:chat_gpt_sdk/src/model/run/response/usage.dart'; + +class CreateRunResponse { + CreateRunResponse({ + required this.metadata, + required this.assistantId, + required this.createdAt, + required this.tools, + required this.completedAt, + required this.threadId, + required this.fileIds, + required this.startedAt, + required this.model, + required this.id, + required this.object, + required this.status, + required this.usage, + required this.instructions, + required this.lastError, + required this.failedAt, + required this.cancelledAt, + required this.expiresAt, + required this.type, + required this.stepDetails, + }); + + Map metadata; + String assistantId; + int createdAt; + List tools; + int completedAt; + String threadId; + List fileIds; + int startedAt; + String model; + String id; + String object; + String status; + Usage? usage; + String instructions; + Map lastError; + int failedAt; + int cancelledAt; + int expiresAt; + String type; + StepDetail? stepDetails; + + factory CreateRunResponse.fromJson(Map json) => + CreateRunResponse( + metadata: json["metadata"] ?? {}, + assistantId: json["assistant_id"] ?? '', + createdAt: json["created_at"] ?? 0, + tools: json["tools"] == null + ? [] + : List.from(json["tools"].map((x) => Tool.fromJson(x))), + completedAt: json["completed_at"] ?? '', + threadId: json["thread_id"] ?? '', + fileIds: json["file_ids"] == null + ? [] + : List.from(json["file_ids"].map((x) => x)), + startedAt: json["started_at"] ?? 0, + model: json["model"] ?? '', + id: json["id"] ?? '', + object: json["object"] ?? '', + status: json["status"] ?? '', + usage: json["usage"] == null ? null : Usage.fromJson(json["usage"]), + instructions: json['instructions'] ?? '', + lastError: json['last_error'] ?? {}, + failedAt: json['failed_at'] ?? 0, + cancelledAt: json['cancelled_at'] ?? 0, + expiresAt: json['expires_at'] ?? 0, + type: json['type'] ?? '', + stepDetails: json['step_details'] == null + ? null + : StepDetail.fromJson(json['step_details']), + ); + + Map toJson() => { + "metadata": metadata, + "assistant_id": assistantId, + "created_at": createdAt, + "usage": usage?.toJson(), + "tools": tools.map((x) => x.toJson()), + "completed_at": completedAt, + "thread_id": threadId, + "file_ids": fileIds, + "started_at": startedAt, + "model": model, + "id": id, + "object": object, + "status": status, + 'instructions': instructions, + 'last_error': lastError, + 'failed_at': failedAt, + 'cancelled_at': cancelledAt, + 'expires_at': expiresAt, + 'type': type, + 'step_details': stepDetails?.toJson(), + }; +} diff --git a/lib/src/model/run/response/create_thread_and_run_data.dart b/lib/src/model/run/response/create_thread_and_run_data.dart new file mode 100644 index 0000000..13d73a0 --- /dev/null +++ b/lib/src/model/run/response/create_thread_and_run_data.dart @@ -0,0 +1,64 @@ +class CreateThreadAndRunData { + CreateThreadAndRunData({ + required this.instructions, + required this.metadata, + required this.assistantId, + required this.createdAt, + required this.tools, + required this.threadId, + required this.expiresAt, + required this.fileIds, + required this.model, + required this.id, + required this.object, + required this.status, + }); + + String instructions; + Map metadata; + String assistantId; + int createdAt; + List> tools; + String threadId; + int expiresAt; + List> fileIds; + String model; + String id; + String object; + String status; + + factory CreateThreadAndRunData.fromJson(Map json) => + CreateThreadAndRunData( + instructions: json["instructions"] ?? '', + metadata: json["metadata"] ?? {}, + assistantId: json["assistant_id"] ?? '', + createdAt: json["created_at"] ?? '', + tools: json["tools"] == null + ? [] + : List>.from(json["tools"].map((x) => x)), + threadId: json["thread_id"] ?? '', + expiresAt: json["expires_at"] ?? '', + fileIds: json["file_ids"] == null + ? [] + : List>.from(json["file_ids"].map((x) => x)), + model: json["model"] ?? '', + id: json["id"] ?? '', + object: json["object"] ?? '', + status: json["status"] ?? '', + ); + + Map toJson() => { + "instructions": instructions, + "metadata": metadata, + "assistant_id": assistantId, + "created_at": createdAt, + "tools": tools, + "thread_id": threadId, + "expires_at": expiresAt, + "file_ids": fileIds, + "model": model, + "id": id, + "object": object, + "status": status, + }; +} diff --git a/lib/src/model/run/response/list_run.dart b/lib/src/model/run/response/list_run.dart new file mode 100644 index 0000000..0214d43 --- /dev/null +++ b/lib/src/model/run/response/list_run.dart @@ -0,0 +1,37 @@ +import 'package:chat_gpt_sdk/src/model/run/response/create_run_response.dart'; + +class ListRun { + ListRun({ + required this.firstId, + required this.data, + required this.lastId, + required this.hasMore, + required this.object, + }); + + String firstId; + List data; + String lastId; + bool hasMore; + String object; + + factory ListRun.fromJson(Map json) => ListRun( + firstId: json["first_id"], + data: json["data"] == null + ? [] + : List.from( + json["data"].map((x) => CreateRunResponse.fromJson(x)), + ), + lastId: json["last_id"], + hasMore: json["has_more"], + object: json["object"], + ); + + Map toJson() => { + "first_id": firstId, + "data": data.map((x) => x.toJson()).toList(), + "last_id": lastId, + "has_more": hasMore, + "object": object, + }; +} diff --git a/lib/src/model/run/response/message_creation.dart b/lib/src/model/run/response/message_creation.dart new file mode 100644 index 0000000..8b17ee6 --- /dev/null +++ b/lib/src/model/run/response/message_creation.dart @@ -0,0 +1,16 @@ +class MessageCreation { + MessageCreation({ + required this.messageId, + }); + + String messageId; + + factory MessageCreation.fromJson(Map json) => + MessageCreation( + messageId: json["message_id"] ?? '', + ); + + Map toJson() => { + "message_id": messageId, + }; +} \ No newline at end of file diff --git a/lib/src/model/run/response/step_detail.dart b/lib/src/model/run/response/step_detail.dart new file mode 100644 index 0000000..3c47e06 --- /dev/null +++ b/lib/src/model/run/response/step_detail.dart @@ -0,0 +1,21 @@ +import 'message_creation.dart'; + +class StepDetail { + StepDetail({ + required this.messageCreation, + required this.type, + }); + + MessageCreation messageCreation; + String type; + + factory StepDetail.fromJson(Map json) => StepDetail( + messageCreation: MessageCreation.fromJson(json["message_creation"]), + type: json["type"] ?? '', + ); + + Map toJson() => { + "message_creation": messageCreation.toJson(), + "type": type, + }; +} diff --git a/lib/src/model/run/response/tool.dart b/lib/src/model/run/response/tool.dart new file mode 100644 index 0000000..1b5a9f5 --- /dev/null +++ b/lib/src/model/run/response/tool.dart @@ -0,0 +1,15 @@ +class Tool { + Tool({ + required this.type, + }); + + String type; + + factory Tool.fromJson(Map json) => Tool( + type: json["type"], + ); + + Map toJson() => { + "type": type, + }; +} \ No newline at end of file diff --git a/lib/src/model/run/response/usage.dart b/lib/src/model/run/response/usage.dart new file mode 100644 index 0000000..925f308 --- /dev/null +++ b/lib/src/model/run/response/usage.dart @@ -0,0 +1,24 @@ + +class Usage { + Usage({ + required this.completionTokens, + required this.promptTokens, + required this.totalTokens, + }); + + int completionTokens; + int promptTokens; + int totalTokens; + + factory Usage.fromJson(Map json) => Usage( + completionTokens: json["completion_tokens"] ?? 0, + promptTokens: json["prompt_tokens"] ?? 0, + totalTokens: json["total_tokens"] ?? 0, + ); + + Map toJson() => { + "completion_tokens": completionTokens, + "prompt_tokens": promptTokens, + "total_tokens": totalTokens, + }; +} \ No newline at end of file diff --git a/lib/src/runs.dart b/lib/src/runs.dart new file mode 100644 index 0000000..67ad698 --- /dev/null +++ b/lib/src/runs.dart @@ -0,0 +1,137 @@ +import 'package:chat_gpt_sdk/chat_gpt_sdk.dart'; +import 'package:chat_gpt_sdk/src/client/client.dart'; +import 'package:chat_gpt_sdk/src/model/run/request/create_run.dart'; +import 'package:chat_gpt_sdk/src/model/run/request/create_thread_and_run.dart'; +import 'package:chat_gpt_sdk/src/model/run/response/create_run_response.dart'; +import 'package:chat_gpt_sdk/src/model/run/response/create_thread_and_run_data.dart'; +import 'package:chat_gpt_sdk/src/model/run/response/list_run.dart'; + +class Runs { + final OpenAIClient _client; + final Map _headers; + + Runs({required OpenAIClient client, required Map headers}) + : _client = client, + _headers = headers; + + ///Create a run. + ///[createRun] + Future createRun({ + required String threadId, + required CreateRun request, + }) { + return _client.post( + _client.apiUrl + kThread + "/$threadId/$kRuns", + request.toJson(), + headers: _headers, + onSuccess: CreateRunResponse.fromJson, + onCancel: (cancelData) => null, + ); + } + + ///Create a thread and run it in one request. + ///[createThreadAndRun] + Future createThreadAndRun({ + required CreateThreadAndRun request, + }) { + return _client.post( + _client.apiUrl + kThread + "/$kRuns", + request.toJson(), + headers: _headers, + onSuccess: CreateThreadAndRunData.fromJson, + onCancel: (cancelData) => null, + ); + } + + Future listRuns({ + required String threadId, + int limit = 20, + String order = 'desc', + String? after, + String? before, + }) { + String url = after != null || before != null + ? _client.apiUrl + + kThread + + "/$threadId/$kRuns?limit=$limit&order=$order&after=$after&before=$before" + : _client.apiUrl + + kThread + + "/$threadId/$kRuns?limit=$limit&order=$order"; + + return _client.get( + url, + headers: _headers, + onSuccess: ListRun.fromJson, + onCancel: (cancelData) => null, + ); + } + + Future listRunSteps({ + required String threadId, + required String runId, + int limit = 20, + String order = 'desc', + String? after, + String? before, + }) { + String url = after != null || before != null + ? _client.apiUrl + + kThread + + "/$threadId/$kRuns/$runId/steps?limit=$limit&order=$order&after=$after&before=$before" + : _client.apiUrl + + kThread + + "/$threadId/$kRuns/$runId/steps?limit=$limit&order=$order"; + + return _client.get( + url, + headers: _headers, + onSuccess: ListRun.fromJson, + onCancel: (cancelData) => null, + ); + } + + Future retrieveRun({ + required String threadId, + required String runId, + }) { + return _client.get( + _client.apiUrl + kThread + "/$threadId/$kRuns/$runId", + headers: _headers, + onSuccess: CreateRunResponse.fromJson, + onCancel: (cancelData) => null, + ); + } + + Future retrieveRunStep({ + required String threadId, + required String runId, + required String stepId, + }) { + return _client.get( + _client.apiUrl + kThread + "/$threadId/$kRuns/$runId/steps/$stepId", + headers: _headers, + onSuccess: CreateRunResponse.fromJson, + onCancel: (cancelData) => null, + ); + } + + /// + /// [metadata] + /// Set of 16 key-value pairs that can be attached to an object. + /// This can be useful for storing additional information about + /// the object in a structured format. Keys can be a maximum of 64 + /// characters long and values can be a maxium of 512 characters long. + Future modifyRun({ + required String threadId, + required String runId, + required Map metadata, + }) { + return _client.post( + _client.apiUrl + "$kThread/$threadId/$kRuns/$runId", + metadata, + headers: _headers, + onSuccess: CreateRunResponse.fromJson, + onCancel: (cancelData) => null, + ); + } +} diff --git a/lib/src/threads.dart b/lib/src/threads.dart index fd884e8..de9847f 100644 --- a/lib/src/threads.dart +++ b/lib/src/threads.dart @@ -1,5 +1,6 @@ import 'package:chat_gpt_sdk/src/client/openai_client.dart'; import 'package:chat_gpt_sdk/src/messages.dart'; +import 'package:chat_gpt_sdk/src/runs.dart'; import 'package:chat_gpt_sdk/src/model/thread/request/thread_request.dart'; import 'package:chat_gpt_sdk/src/model/thread/response/thread_delete_response.dart'; import 'package:chat_gpt_sdk/src/model/thread/response/thread_response.dart'; @@ -72,4 +73,7 @@ class Threads { ///messages Messages get messages => Messages(client: _client, headers: headersAssistants); + + ///runs + Runs get runs => Runs(client: _client, headers: headersAssistants); } diff --git a/lib/src/utils/constants.dart b/lib/src/utils/constants.dart index 04afc25..d477394 100644 --- a/lib/src/utils/constants.dart +++ b/lib/src/utils/constants.dart @@ -54,6 +54,9 @@ const kAssistants = 'assistants'; ///messages const kMessages = 'messages'; +///runs +const kRuns = 'runs'; + ///model name const kGpt3TurboInstruct = 'gpt-3.5-turbo-instruct';