Skip to content

Commit

Permalink
Merge pull request #5 from Uralstech/unstable
Browse files Browse the repository at this point in the history
UGemini v1.1.1
  • Loading branch information
Uralstech committed Jul 5, 2024
2 parents 73a5552 + 1d5b112 commit 8c47c1c
Show file tree
Hide file tree
Showing 10 changed files with 424 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace Uralstech.UGemini.FileAPI
/// <summary>
/// The response for a <see cref="GeminiFileListRequest"/> call.
/// </summary>
[JsonObject(NamingStrategyType = typeof (CamelCaseNamingStrategy))]
[JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))]
public class GeminiFileListResponse
{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System.ComponentModel;

namespace Uralstech.UGemini.FileAPI
{
/// <summary>
/// Metadata for a <see cref="GeminiFile"/> to be uploaded.
/// </summary>
[JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))]
public class GeminiFileUploadMetaData
{
/// <summary>
/// The <see cref="GeminiFileUploadRequest"/> resource name. This does not work right now.
/// </summary>
/// <remarks>
/// The ID (name excluding the "files/" prefix) can contain up to 40 characters that are lowercase alphanumeric or dashes (-).<br/>
/// The ID cannot start or end with a dash. If the name is empty on create, a unique name will be generated. Example: files/123-456
/// </remarks>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore), DefaultValue(null)]
public string Name = null;

/// <summary>
/// The human-readable display name for the <see cref="GeminiFileUploadRequest"/>. The display name must be no more than 512 characters in length, including spaces. Example: "Welcome Image"
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore), DefaultValue(null)]
public string DisplayName = null;
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,12 @@ namespace Uralstech.UGemini.FileAPI
/// This feature is currently being worked on and is unstable.
/// </remarks>
[JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))]
public class GeminiFileUploadRequest : IGeminiPostRequest
public class GeminiFileUploadRequest : IGeminiMultiPartPostRequest
{
// /// <summary>
// /// The <see cref="GeminiFileUploadRequest"/> resource name.
// /// </summary>
// /// <remarks>
// /// The ID (name excluding the "files/" prefix) can contain up to 40 characters that are lowercase alphanumeric or dashes (-).<br/>
// /// The ID cannot start or end with a dash. If the name is empty on create, a unique name will be generated. Example: files/123-456
// /// </remarks>
// public string Name;

// /// <summary>
// /// The human-readable display name for the <see cref="GeminiFileUploadRequest"/>. The display name must be no more than 512 characters in length, including spaces. Example: "Welcome Image"
// /// </summary>
// [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore), DefaultValue(null)]
// public string DisplayName = null;
/// <summary>
/// Metadata for the <see cref="GeminiFile"/> to be uploaded.
/// </summary>
public GeminiFileUploadMetaData File;

/// <summary>
/// The IANA standard MIME type of the <see cref="GeminiFileUploadRequest"/>.
Expand Down Expand Up @@ -71,9 +61,21 @@ public GeminiFileUploadRequest(string contentType, bool useBetaApi = true)
}

/// <inheritdoc/>
public string GetUtf8EncodedData()
public string GetUtf8EncodedData(string dataSeperator)
{
return Encoding.UTF8.GetString(RawData);
StringBuilder data = new($"--{dataSeperator}\r\n");

data.Append("Content-Disposition: form-data; name=\"metadata\"\r\n");
data.Append("Content-Type: application/json; charset=UTF-8\r\n\r\n");
data.Append($"{JsonConvert.SerializeObject(this)}\r\n");

data.Append($"--{dataSeperator}\r\n");
data.Append("Content-Disposition: form-data; name=\"file\"\r\n");
data.Append($"Content-Type: {ContentType}\r\n\r\n");
data.Append($"{Encoding.UTF8.GetString(RawData)}\r\n");
data.Append($"--{dataSeperator}--\r\n");

return data.ToString();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace Uralstech.UGemini
{
/// <summary>
/// All Gemini API POST requests with multi-part data must inherit from this interface.
/// </summary>
public interface IGeminiMultiPartPostRequest : IGeminiRequest
{
/// <summary>
/// Converts the request object to a UTF-8 encoded multi-part <see cref="string"/>.
/// </summary>
/// <param name="dataSeperator">The boundary to seperate each part of the data.</param>
/// <returns>The string data.</returns>
public string GetUtf8EncodedData(string dataSeperator);
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using Newtonsoft.Json;
using System;
using System.Net;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Networking;
Expand All @@ -15,6 +14,8 @@ namespace Uralstech.UGemini
[AddComponentMenu("Uralstech/UGemini/Gemini API Manager")]
public class GeminiManager : Singleton<GeminiManager>
{
private const string MultiPartFormDataSeperator = "xxxxxxxxxx";

/// <summary>
/// <see href="https://ai.google.dev/gemini-api/docs/models/gemini#gemini-1.0-pro-vision">
/// Note: Gemini 1.0 Pro Vision is deprecated. Use 1.5 Flash or 1.5 Pro instead.
Expand Down Expand Up @@ -91,6 +92,30 @@ public async Task<TResponse> Request<TResponse>(IGeminiPostRequest request)
return JsonConvert.DeserializeObject<TResponse>((await ComputeRequest(webRequest)).downloadHandler.text);
}

/// <summary>
/// Computes a request on the Gemini API.
/// </summary>
///
/// <typeparam name="TResponse">
/// The response type. For example, a request of type <see cref="Chat.GeminiChatRequest"/> corresponds
/// to a response type of <see cref="Chat.GeminiChatResponse"/>, and a request of type <see cref="TokenCounting.GeminiTokenCountRequest"/>
/// corresponds to a response of type <see cref="TokenCounting.GeminiTokenCountResponse"/>.
/// </typeparam>
///
/// <param name="request">The request object.</param>
/// <returns>The computed response.</returns>
/// <exception cref="GeminiRequestException">Thrown when the API request fails.</exception>
public async Task<TResponse> Request<TResponse>(IGeminiMultiPartPostRequest request)
{
string requestEndpoint = request.EndpointUri;
string requestData = request.GetUtf8EncodedData(MultiPartFormDataSeperator);

using UnityWebRequest webRequest = UnityWebRequest.Post(requestEndpoint, requestData, $"multipart/related; boundary={MultiPartFormDataSeperator}");
webRequest.SetRequestHeader("X-Goog-Upload-Protocol", "multipart");

return JsonConvert.DeserializeObject<TResponse>((await ComputeRequest(webRequest)).downloadHandler.text);
}

/// <summary>
/// Computes a request on the Gemini API.
/// </summary>
Expand Down
Loading

0 comments on commit 8c47c1c

Please sign in to comment.