Skip to content

Commit

Permalink
Add batch translation support
Browse files Browse the repository at this point in the history
  • Loading branch information
davc0n committed May 11, 2023
1 parent a993e58 commit 97947b2
Show file tree
Hide file tree
Showing 7 changed files with 361 additions and 1 deletion.
32 changes: 32 additions & 0 deletions src/Model/BatchTranslation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System.Collections.Generic;

namespace ModernMT.Model
{
public class BatchTranslation : Model
{
private readonly dynamic _data;

public Translation Data => _data as Translation;

public List<Translation> DataList
{
get
{
if (_data is List<Translation> list)
return list;

return new List<Translation>{_data};
}

}

public readonly dynamic Metadata;

public BatchTranslation(dynamic data, dynamic metadata)
{
_data = data;
Metadata = metadata;
}

}
}
6 changes: 6 additions & 0 deletions src/Model/TranslateOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,11 @@ public class TranslateOptions : Model

[JsonProperty("alt_translations")]
public int AltTranslations { get; set; }

[JsonProperty("metadata")]
public dynamic Metadata { get; set; }

// not in json body but in request headers
public string IdempotencyKey { get; set; }
}
}
4 changes: 3 additions & 1 deletion src/ModernMTException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ public class ModernMTException : Exception
{
public readonly int Code;
public readonly string Type;
public readonly dynamic Metadata;

public ModernMTException(int code, string type, string message) : base(message)
public ModernMTException(int code, string type, string message, dynamic metadata = null) : base(message)
{
Code = code;
Type = type;
Metadata = metadata;
}

public override string ToString()
Expand Down
164 changes: 164 additions & 0 deletions src/ModernMTService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
using System.IO;
using System.Linq;
using System.Net.Http;
using JWT.Algorithms;
using JWT.Builder;
using JWT.Exceptions;

namespace ModernMT
{
Expand All @@ -18,6 +21,9 @@ public class ModernMTService
private readonly ModernMTClient _client;
public readonly MemoryServices Memories;

private JwtBuilder _jwtBuilder;
private long _batchPublicKeyTimestampSec;

public ModernMTService(string apiKey, string platform = Platform, string platformVersion = PlatformVersion,
long apiClient = 0)
{
Expand Down Expand Up @@ -111,6 +117,164 @@ public List<Translation> TranslateWithKeys(string source, string target, List<st
return _client.Send<List<Translation>>("get", "/translate", data);
}

public bool BatchTranslate(string webhook, string source, string target, string q, TranslateOptions options)
{
return BatchTranslate(webhook, source, target, q, null, null, options);
}

public bool BatchTranslate(string webhook, string source, string target, List<string> q, TranslateOptions options)
{
return BatchTranslate(webhook, source, target, q, null, null, options);
}

public bool BatchTranslate(string webhook, string source, string target, string q, long[] hints = null,
string contextVector = null, TranslateOptions options = null)
{
string[] hintsArray = null;
if (hints != null)
hintsArray = hints.Select(el => el.ToString()).ToArray();

return BatchTranslateWithKeys(webhook, source, target, (object) q, hintsArray, contextVector, options);
}

public bool BatchTranslate(string webhook, string source, string target, List<string> q, long[] hints = null,
string contextVector = null, TranslateOptions options = null)
{
string[] hintsArray = null;
if (hints != null)
hintsArray = hints.Select(el => el.ToString()).ToArray();

return BatchTranslateWithKeys(webhook, source, target, (object) q, hintsArray, contextVector, options);
}

public bool BatchTranslateWithKeys(string webhook, string source, string target, string q,
string[] hints = null, string contextVector = null, TranslateOptions options = null)
{
return BatchTranslateWithKeys(webhook, source, target, (object) q, hints, contextVector, options);
}

public bool BatchTranslateWithKeys(string webhook, string source, string target, List<string> q,
string[] hints = null, string contextVector = null, TranslateOptions options = null)
{
return BatchTranslateWithKeys(webhook, source, target, (object) q, hints, contextVector, options);
}

public bool BatchTranslateWithKeys(string webhook, string source, string target, object q,
string[] hints = null, string contextVector = null, TranslateOptions options = null)
{
var headers = new Dictionary<string, string>();
var data = new Dictionary<string, dynamic>
{
{ "webhook", webhook },
{ "source", source },
{ "target", target },
{ "q", q },
{ "hints", hints != null ? string.Join(",", hints) : null },
{ "context_vector", contextVector },
};

if (options != null)
{
data.Add("project_id", options.ProjectId);
data.Add("multiline", options.Multiline);
data.Add("format", options.Format);
data.Add("alt_translations", options.AltTranslations);
data.Add("metadata", options.Metadata);

headers.Add("x-idempotency-key", options.IdempotencyKey);
}

var res = _client.Send<Dictionary<string, dynamic>>("post", "/translate/batch", data, null, headers);
return res["enqueued"];
}

public BatchTranslation HandleCallback(dynamic body, string signature)
{
if (_jwtBuilder == null)
RefreshJwtBuilder();

if (DateTimeOffset.Now.ToUnixTimeSeconds() - _batchPublicKeyTimestampSec > 3600) // key is older than 1 hour
{
try
{
RefreshJwtBuilder();
}
catch (Exception)
{
// ignore
}
}

try
{
// ReSharper disable once PossibleNullReferenceException
_jwtBuilder.Decode(signature);
}
catch (SignatureVerificationException e)
{
throw new SignatureException(e);
}

JObject json;
switch (body)
{
case string _:
json = JObject.Parse(body);
break;
case JObject _:
json = body;
break;
default:
json = JObject.FromObject(body);
break;
}

// ReSharper disable once PossibleNullReferenceException
var resultJson = json["result"].ToObject<JObject>();

// ReSharper disable once PossibleNullReferenceException
dynamic metadata = null;
if (json.TryGetValue("metadata", out var metadataJson))
metadata = metadataJson.ToObject<dynamic>();

// ReSharper disable once PossibleNullReferenceException
var status = resultJson["status"].ToObject<int>();
if (status >= 300 || status < 200)
{
// ReSharper disable once PossibleNullReferenceException
var error = resultJson["error"].ToObject<dynamic>();
throw new ModernMTException(status, (string) error.type, (string) error.message, metadata);
}

var data = resultJson["data"];
if (data is JArray)
return new BatchTranslation(data.ToObject<List<Translation>>(), metadata);
else
// ReSharper disable once PossibleNullReferenceException
return new BatchTranslation(data.ToObject<Translation>(), metadata);
}

private void RefreshJwtBuilder()
{
var res = _client.Send<Dictionary<string, dynamic>>("get", "/translate/batch/key");
string encodedPublicKey = res["publicKey"];

var keyBytes = Convert.FromBase64String(encodedPublicKey);
var keyString = System.Text.Encoding.UTF8.GetString(keyBytes);

keyString = keyString
.Replace("-----BEGIN PUBLIC KEY-----", "")
.Replace("-----END PUBLIC KEY-----", "");

var rsa = Utils.DecodeX509PublicKey(Convert.FromBase64String(keyString));

_jwtBuilder = JwtBuilder.Create()
.WithAlgorithm(new RS256Algorithm(rsa))
.MustVerifySignature();

_batchPublicKeyTimestampSec = DateTimeOffset.Now.ToUnixTimeSeconds();
}

public string GetContextVector(string source, string targets, string text, long[] hints = null, int limit = 0)
{
var res = GetContextVector(source, new List<string> { targets }, text, hints, limit);
Expand Down
13 changes: 13 additions & 0 deletions src/SignatureException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using JWT.Exceptions;

namespace ModernMT
{
public class SignatureException : ModernMTException
{
public SignatureException(SignatureVerificationException e)
: base(0, "SignatureException", e.Message)
{

}
}
}
Loading

0 comments on commit 97947b2

Please sign in to comment.