absolute utter filth yet it's working

This commit is contained in:
femsci 2023-11-17 22:46:30 +01:00
parent c3f3cc6acb
commit 7b322ef7fb
Signed by: femsci
GPG key ID: 08F7911F0E650C67
11 changed files with 169 additions and 4 deletions

View file

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Nyanlabs.Umogen.Core.Models;
public class ApiChoice
{
public string FinishReason { get; set; } = default!;
public int Index { get; set; }
public GptMessage? Message { get; set; }
public GptMessage? Delta { get; set; }
}

View file

@ -4,4 +4,7 @@ public record ApiRequest
{
public string Model { get; set; } = "gpt-3.5-turbo";
public float Temperature { get; set; } = 0.7f;
public ICollection<GptMessage> Messages { get; set; } = new List<GptMessage>();
public bool Stream { get; set; }
//public ApiResponseFormat ResponseFormat { get; set; } = new();
}

View file

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Nyanlabs.Umogen.Core.Models;
public class ApiResponse
{
public string Id { get; set; } = default!;
public string Model { get; set; } = default!;
public string Object { get; set; } = default!;
public UsageStats Usage { get; set; } = default!;
public ICollection<ApiChoice> Choices { get; set; } = new List<ApiChoice>();
public long Created { get; set; }
}

View file

@ -0,0 +1,6 @@
namespace Nyanlabs.Umogen.Core.Models;
public class ApiResponseFormat
{
public string Type { get; set; } = "json_object";
}

View file

@ -0,0 +1,7 @@
namespace Nyanlabs.Umogen.Core.Models;
public record GptMessage(string Role, string Content)
{
public string Role { get; } = Role;
public string Content { get; } = Content;
}

View file

@ -45,6 +45,6 @@ public class UmoDocument : INLSerializable
public ValidTime? ValidTime { get; set; }
public string NLSerialize()
{
return $"{Doctype.Name().Item1} for {Employee.NLSerialize()} working for {Employer.NLSerialize()}; valid {ValidTime?.NLSerialize() ?? "for unspecified time"}.";
return $"{Doctype.Name().Item2} for {Employee.NLSerialize()} working for {Employer.NLSerialize()}; valid {ValidTime?.NLSerialize() ?? "for unspecified time"}.";
}
}

View file

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace Nyanlabs.Umogen.Core.Models;
public class UsageStats
{
[JsonPropertyName("completion_tokens")]
public int CompletionTokens { get; set; }
[JsonPropertyName("prompt_tokens")]
public int PromptTokens { get; set; }
[JsonPropertyName("total_tokens")]
public int TotalTokens { get; set; }
}

View file

@ -38,7 +38,7 @@ public class UmoEngine : IDisposable
_http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
}
private readonly HttpClient _http;
internal readonly HttpClient _http;
public async Task<bool> ValidateKey()
{

View file

@ -1,3 +1,10 @@
using System.Data;
using System.Net.Http.Json;
using System.Text;
using System.Text.Json;
using Nyanlabs.Umogen.Core.Models;
namespace Nyanlabs.Umogen.Core;
public class UmoProcess
@ -9,8 +16,56 @@ public class UmoProcess
private readonly UmoEngine _eng;
public async Task Ask()
public async IAsyncEnumerable<string> Ask(string content)
{
ApiRequest req = new()
{
Messages = new List<GptMessage>()
{
new("system", Umogen.PROMPT),
new("user", content)
},
Stream = true
};
HttpRequestMessage msg = new(HttpMethod.Post, "chat/completions");
msg.Content = new StringContent(JsonSerializer.Serialize(req, options: Umogen.JSON_OPTS), Encoding.UTF8, "application/json");
var resp = await _eng._http.SendAsync(msg, HttpCompletionOption.ResponseHeadersRead);
if (!resp.IsSuccessStatusCode)
{
Console.WriteLine(await resp.Content.ReadAsStringAsync());
throw new DataException($"Cannot process request: {resp.StatusCode}.");
}
var apiResp = await resp.Content.ReadAsStreamAsync();
var reader = new StreamReader(apiResp);
string? line;
while ((line = await reader.ReadLineAsync()) != null)
{
if (line.StartsWith("data:"))
{
line = line[5..].Trim();
}
if (string.IsNullOrWhiteSpace(line))
{
continue;
}
File.AppendAllText("/tmp/raw.md", line + "\n");
ApiResponse respChunk = JsonSerializer.Deserialize<ApiResponse>(line, options: Umogen.JSON_OPTS)!;
ApiChoice choice = respChunk.Choices.First();
if (choice.FinishReason == "stop")
{
break;
}
yield return choice.Delta?.Content ?? ":<";
}
}
}

View file

@ -1,7 +1,13 @@
using System.Text.Json;
namespace Nyanlabs.Umogen.Core;
public class Umogen
{
public const string DEFAULT_API_KEY_FILE = "umogenkey.secret";
public const string PROMPT = "You write Polish legal document in markdown format.";
public const string PROMPT = "You write Polish legal contracts in Polish language in markdown format.";
public static readonly JsonSerializerOptions JSON_OPTS = new(JsonSerializerDefaults.Web)
{
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
};
}

View file

@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Nyanlabs.Umogen.Core;
using Nyanlabs.Umogen.Core.Models;
namespace Nyanlabs.Umogen.WebTests;
public class GenerationTests
{
private Person ModelEmployer => new("Kotek", "Miauczyński", "CBS4327563", "04281308999", new DateTime(2004, 08, 13));
private Person ModelEmployee => new("Miau", "Kotczyński", "CBS69696969", "76011694336", new DateTime(1976, 1, 16));
[Fact]
public async Task Meow4Me()
{
// Given
var rootDir = Environment.CurrentDirectory.Replace("test/Nyanlabs.Umogen.WebTests/bin/Debug/net8.0", "");
UmoEngine eng = new(Path.Combine(rootDir, Core.Umogen.DEFAULT_API_KEY_FILE));
// When
UmoDocument doc = new(UmoDoctype.CONTR_EMPLOYMENT, ModelEmployer, ModelEmployee);
UmoProcess proc = new(eng);
var enu = proc.Ask(doc.NLSerialize());
await foreach (var str in enu)
{
Console.Write(str);
File.AppendAllText("/tmp/doc.md", str);
}
Console.WriteLine("~");
}
}