From e6c0ecb4f4a8b87dae784a1b50edbb0366089052 Mon Sep 17 00:00:00 2001 From: Branislav Burdiliak Date: Thu, 15 Feb 2024 09:12:44 +0100 Subject: [PATCH] OpenAIService - new functions/endpoints - modifyAssistant The wrapped API: POST https://api.openai.com/v1/assistants/{assistant_id} --- .../service/OpenAIServiceImpl.scala | 42 +++++++++++--- .../openaiscala/domain/AssistantTool.scala | 2 +- .../openaiscala/service/OpenAIService.scala | 57 ++++++++++++++++++- .../service/OpenAIServiceWrapper.scala | 28 ++++++++- .../examples/ModifyAssistant.scala | 14 +++++ 5 files changed, 133 insertions(+), 10 deletions(-) create mode 100755 openai-examples/src/main/scala/io/cequence/openaiscala/examples/ModifyAssistant.scala diff --git a/openai-client/src/main/scala/io/cequence/openaiscala/service/OpenAIServiceImpl.scala b/openai-client/src/main/scala/io/cequence/openaiscala/service/OpenAIServiceImpl.scala index 79107859..9ee617e4 100644 --- a/openai-client/src/main/scala/io/cequence/openaiscala/service/OpenAIServiceImpl.scala +++ b/openai-client/src/main/scala/io/cequence/openaiscala/service/OpenAIServiceImpl.scala @@ -2,17 +2,15 @@ package io.cequence.openaiscala.service import akka.stream.scaladsl.Source import akka.util.ByteString -import play.api.libs.json.{JsArray, JsObject, JsValue, Json} -import io.cequence.openaiscala.JsonUtil.JsonOps import io.cequence.openaiscala.JsonFormats._ +import io.cequence.openaiscala.JsonUtil.JsonOps import io.cequence.openaiscala.OpenAIScalaClientException -import io.cequence.openaiscala.domain.settings._ import io.cequence.openaiscala.domain.response._ +import io.cequence.openaiscala.domain.settings._ import io.cequence.openaiscala.domain.{ AssistantTool, BaseMessage, ChatRole, - FileId, FunctionSpec, SortOrder, Thread, @@ -21,6 +19,7 @@ import io.cequence.openaiscala.domain.{ ThreadMessageFile, ToolSpec } +import play.api.libs.json.{JsObject, JsValue, Json} import java.io.File import scala.concurrent.Future @@ -770,11 +769,14 @@ private trait OpenAIServiceImpl extends OpenAICoreServiceImpl with OpenAIService * Retrieves an AssistantFile. * * @param assistantId - * The ID of the assistant who the file belongs to. + * The ID of the assistant who the file belongs to. * @param fileId - * The ID of the file we're getting. + * The ID of the file we're getting. */ - override def retrieveAssistantFile(assistantId: String, fileId: String): Future[Option[AssistantFile]] = + override def retrieveAssistantFile( + assistantId: String, + fileId: String + ): Future[Option[AssistantFile]] = execGETWithStatus( EndPoint.assistants, Some(s"$assistantId/files/$fileId") @@ -782,4 +784,30 @@ private trait OpenAIServiceImpl extends OpenAICoreServiceImpl with OpenAIService handleNotFoundAndError(response).map(_.asSafe[AssistantFile]) } + override def modifyAssistant( + assistantId: String, + model: Option[String], + name: Option[String], + description: Option[String], + instructions: Option[String], + tools: Seq[AssistantTool], + fileIds: Seq[String], + metadata: Map[String, String] + ): Future[Option[Assistant]] = + execPOSTWithStatus( + EndPoint.assistants, + endPointParam = Some(assistantId), + bodyParams = jsonBodyParams( + Param.model -> model, + Param.name -> name, + Param.description -> description, + Param.instructions -> instructions, + Param.tools -> Some(Json.toJson(tools)), + Param.file_ids -> (if (fileIds.nonEmpty) Some(fileIds) else None), + Param.metadata -> (if (metadata.nonEmpty) Some(metadata) else None) + ) + ).map { response => + handleNotFoundAndError(response).map(_.asSafe[Assistant]) + } + } diff --git a/openai-core/src/main/scala/io/cequence/openaiscala/domain/AssistantTool.scala b/openai-core/src/main/scala/io/cequence/openaiscala/domain/AssistantTool.scala index 7b1522fa..470bb2b8 100644 --- a/openai-core/src/main/scala/io/cequence/openaiscala/domain/AssistantTool.scala +++ b/openai-core/src/main/scala/io/cequence/openaiscala/domain/AssistantTool.scala @@ -28,7 +28,7 @@ object AssistantTool { // FIXME: description should be optional in request and mandatory in response description: String, name: String, - parameters: Option[String] + parameters: Option[String] // TODO: check representation ) } diff --git a/openai-core/src/main/scala/io/cequence/openaiscala/service/OpenAIService.scala b/openai-core/src/main/scala/io/cequence/openaiscala/service/OpenAIService.scala index 01d65fac..f785b9e3 100644 --- a/openai-core/src/main/scala/io/cequence/openaiscala/service/OpenAIService.scala +++ b/openai-core/src/main/scala/io/cequence/openaiscala/service/OpenAIService.scala @@ -760,7 +760,10 @@ trait OpenAIService extends OpenAICoreService { * @param fileId * A File ID (with purpose="assistants") that the assistant should use. Useful for tools * like `retrieval` and `code_interpreter` that can access files. - * @return + * @see + * OpenAI + * Doc */ def createAssistantFile( assistantId: String, @@ -786,6 +789,10 @@ trait OpenAIService extends OpenAICoreService { * list. For instance, if you make a list request and receive 100 objects, ending with * `obj_foo`, your subsequent call can include `before=obj_foo`` in order to fetch the * previous page of the list. + * @see + * OpenAI + * Doc */ def listAssistants( limit: Option[Int] = None, // TODO: default 20 or None? @@ -817,6 +824,9 @@ trait OpenAIService extends OpenAICoreService { * list. For instance, if you make a list request and receive 100 objects, ending with * `obj_foo`, your subsequent call can include `before=obj_foo` in order to fetch the * previous page of the list. + * OpenAI + * Doc */ def listAssistantFiles( assistantId: String, @@ -831,6 +841,9 @@ trait OpenAIService extends OpenAICoreService { * * @param assistantId * The ID of the assistant to retrieve. + * OpenAI + * Doc */ def retrieveAssistant(assistantId: String): Future[Option[Assistant]] @@ -841,7 +854,49 @@ trait OpenAIService extends OpenAICoreService { * The ID of the assistant who the file belongs to. * @param fileId * The ID of the file we're getting. + * OpenAI + * Doc */ def retrieveAssistantFile(assistantId: String, fileId: String): Future[Option[AssistantFile]] + /** + * Modifies an assistant. + * + * @param assistantId + * @param model + * ID of the model to use. You can use the List models API to see all of your available models, + * or see our Model overview for descriptions of them. + * @param name + * The name of the assistant. The maximum length is 256 characters. + * @param description + * The description of the assistant. The maximum length is 512 characters. + * @param instructions + * The system instructions that the assistant uses. The maximum length is 32768 characters. + * @param tools + * A list of tool enabled on the assistant. There can be a maximum of 128 tools per assistant. Tools can be of types + * code_interpreter, retrieval, or function. + * @param fileIds + * A list of File IDs attached to this assistant. There can be a maximum of 20 files attached to the assistant. + * Files are ordered by their creation date in ascending order. If a file was previously attached to the list but + * does not show up in the list, it will be deleted from the assistant. + * @param 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. + * OpenAI + * Doc + */ + def modifyAssistant( + assistantId: String, + model: Option[String] = None, + name: Option[String] = None, + description: Option[String] = None, + instructions: Option[String] = None, + tools: Seq[AssistantTool] = Seq.empty[AssistantTool], + fileIds: Seq[String] = Seq.empty, + metadata: Map[String, String] = Map.empty + ): Future[Option[Assistant]] + } diff --git a/openai-core/src/main/scala/io/cequence/openaiscala/service/OpenAIServiceWrapper.scala b/openai-core/src/main/scala/io/cequence/openaiscala/service/OpenAIServiceWrapper.scala index 30447b41..97d64459 100644 --- a/openai-core/src/main/scala/io/cequence/openaiscala/service/OpenAIServiceWrapper.scala +++ b/openai-core/src/main/scala/io/cequence/openaiscala/service/OpenAIServiceWrapper.scala @@ -338,9 +338,35 @@ trait OpenAIServiceWrapper extends OpenAIService { override def retrieveAssistant(assistantId: String): Future[Option[Assistant]] = wrap(_.retrieveAssistant(assistantId)) - override def retrieveAssistantFile(assistantId: String, fileId: String): Future[Option[AssistantFile]] = + override def retrieveAssistantFile( + assistantId: String, + fileId: String + ): Future[Option[AssistantFile]] = wrap(_.retrieveAssistantFile(assistantId, fileId)) + override def modifyAssistant( + assistantId: String, + model: Option[String], + name: Option[String], + description: Option[String], + instructions: Option[String], + tools: Seq[AssistantTool], + fileIds: Seq[String], + metadata: Map[String, String] + ): Future[Option[Assistant]] = + wrap( + _.modifyAssistant( + assistantId, + model, + name, + description, + instructions, + tools, + fileIds, + metadata + ) + ) + protected def wrap[T]( fun: OpenAIService => Future[T] ): Future[T] diff --git a/openai-examples/src/main/scala/io/cequence/openaiscala/examples/ModifyAssistant.scala b/openai-examples/src/main/scala/io/cequence/openaiscala/examples/ModifyAssistant.scala new file mode 100755 index 00000000..f3f1d5a4 --- /dev/null +++ b/openai-examples/src/main/scala/io/cequence/openaiscala/examples/ModifyAssistant.scala @@ -0,0 +1,14 @@ +package io.cequence.openaiscala.examples + +object ModifyAssistant extends Example { + + override protected def run = + for { + thread <- service.modifyAssistant( + assistantId = "asst_Btbc2h7dqyDU52g1KfBa2XE8", + metadata = Map("user_id" -> "986415", "due_date" -> "2028-08-01") + ) + } yield { + println(thread) + } +}