Refactor Server Sent Events parsing

This commit is contained in:
femsci 2023-11-17 23:38:35 +01:00
parent b9c218111a
commit 067a5365d2
Signed by: femsci
GPG key ID: 08F7911F0E650C67
6 changed files with 51 additions and 37 deletions

View file

@ -1,8 +1,3 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Nyanlabs.Umogen.Core.Models; namespace Nyanlabs.Umogen.Core.Models;
public class ApiChoice public class ApiChoice

View file

@ -1,8 +1,3 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Nyanlabs.Umogen.Core.Models; namespace Nyanlabs.Umogen.Core.Models;
public class ApiResponse public class ApiResponse

View file

@ -1,8 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace Nyanlabs.Umogen.Core.Models; namespace Nyanlabs.Umogen.Core.Models;

View file

@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
namespace Nyanlabs.Umogen.Core;
public class SseConsumer<TEvent> : IDisposable, IAsyncDisposable
{
public SseConsumer(Stream stream)
{
_stream = stream;
}
private readonly Stream _stream;
public async IAsyncEnumerable<TEvent> ReadEvents()
{
var reader = new StreamReader(_stream);
while (!reader.EndOfStream)
{
string line = (await reader.ReadLineAsync())!;
//Second \n
await reader.ReadLineAsync();
//Remove 'data: '
line = line[6..].Trim();
yield return JsonSerializer.Deserialize<TEvent>(line, Umogen.JSON_OPTS) ?? throw new DataException("Invalid data...");
}
}
public void Dispose()
{
_stream.Dispose();
}
public ValueTask DisposeAsync()
{
return _stream.DisposeAsync();
}
}

View file

@ -1,5 +1,4 @@
using System.Data; using System.Data;
using System.Net.Http.Json;
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
@ -38,34 +37,18 @@ public class UmoProcess
throw new DataException($"Cannot process request: {resp.StatusCode}."); throw new DataException($"Cannot process request: {resp.StatusCode}.");
} }
var apiResp = await resp.Content.ReadAsStreamAsync(); var apiStream = await resp.Content.ReadAsStreamAsync();
var reader = new StreamReader(apiResp); using var sseConsumer = new SseConsumer<ApiResponse>(apiStream);
await foreach (var chunk in sseConsumer.ReadEvents())
string? line;
while ((line = await reader.ReadLineAsync()) != null)
{ {
if (line.StartsWith("data:")) ApiChoice choice = chunk.Choices.First();
{
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") if (choice.FinishReason == "stop")
{ {
break; break;
} }
yield return choice.Delta?.Content ?? ":<"; yield return choice.Delta?.Content ?? throw new DataException("Message delta is null");
} }
} }
} }

View file

@ -5,7 +5,7 @@ namespace Nyanlabs.Umogen.Core;
public class Umogen public class Umogen
{ {
public const string DEFAULT_API_KEY_FILE = "umogenkey.secret"; public const string DEFAULT_API_KEY_FILE = "umogenkey.secret";
public const string PROMPT = "You write Polish legal contracts in Polish language in markdown format."; public const string PROMPT = "You write Polish legal contracts in Polish language in markdown format. Fill unknown fields with signature placeholder (underscore) with enough space to sign it with pen.";
public static readonly JsonSerializerOptions JSON_OPTS = new(JsonSerializerDefaults.Web) public static readonly JsonSerializerOptions JSON_OPTS = new(JsonSerializerDefaults.Web)
{ {
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower