-
Notifications
You must be signed in to change notification settings - Fork 124
Canonicalize classified stream failures #438
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Seth Juarez (sethjuarez)
wants to merge
10
commits into
main
Choose a base branch
from
sejuare-microsoft/canonical-stream-failures
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 3 commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
a0d9f2d
feat(schema): classify stream failures
sethjuarez 2abeb65
feat(rust): bridge canonical stream failures
sethjuarez fc23a1a
feat(typescript): reconcile classified stream failures
sethjuarez 22351d0
fix(typescript): close failed provider streams
sethjuarez 8d0ddac
fix(harness): journal denied tool results
sethjuarez 8a04ccc
fix(schema): normalize prompt sample whitespace
sethjuarez 2f98bec
fix(python): restore runtime CI checks
sethjuarez 242e27f
test(schema): generate failure chunk round trips
sethjuarez e751363
test(schema): cover failure discriminator round trips
sethjuarez ae908bd
fix(typescript): close cancelled provider streams
sethjuarez File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
11 changes: 11 additions & 0 deletions
11
runtime/csharp/Prompty.Core.Tests/Model/events/FailureChunkConversionTests.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| // <auto-generated by typra-emitter> | ||
| using Xunit; | ||
|
|
||
| #pragma warning disable IDE0130 | ||
| namespace Prompty.Core; | ||
| #pragma warning restore IDE0130 | ||
|
|
||
|
|
||
| public class FailureChunkConversionTests | ||
| { | ||
| } |
123 changes: 123 additions & 0 deletions
123
runtime/csharp/Prompty.Core.Tests/Model/events/StreamFailureConversionTests.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,123 @@ | ||
| // <auto-generated by typra-emitter> | ||
| using Xunit; | ||
|
|
||
| #pragma warning disable IDE0130 | ||
| namespace Prompty.Core; | ||
| #pragma warning restore IDE0130 | ||
|
|
||
|
|
||
| public class StreamFailureConversionTests | ||
| { | ||
| [Fact] | ||
| public void LoadYamlInput() | ||
| { | ||
| string yamlData = """ | ||
| outcome: indeterminate | ||
| message: "SSE stream error: connection reset" | ||
|
|
||
| """; | ||
|
|
||
| var instance = StreamFailure.FromYaml(yamlData); | ||
|
|
||
| Assert.NotNull(instance); | ||
| Assert.Equal(StreamFailureOutcome.Indeterminate, instance.Outcome); | ||
| Assert.Equal("SSE stream error: connection reset", instance.Message); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void LoadJsonInput() | ||
| { | ||
| string jsonData = """ | ||
| { | ||
| "outcome": "indeterminate", | ||
| "message": "SSE stream error: connection reset" | ||
| } | ||
| """; | ||
|
|
||
| var instance = StreamFailure.FromJson(jsonData); | ||
| Assert.NotNull(instance); | ||
| Assert.Equal(StreamFailureOutcome.Indeterminate, instance.Outcome); | ||
| Assert.Equal("SSE stream error: connection reset", instance.Message); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void RoundtripJson() | ||
| { | ||
| // Test that FromJson -> ToJson -> FromJson produces equivalent data | ||
| string jsonData = """ | ||
| { | ||
| "outcome": "indeterminate", | ||
| "message": "SSE stream error: connection reset" | ||
| } | ||
| """; | ||
|
|
||
| var original = StreamFailure.FromJson(jsonData); | ||
| Assert.NotNull(original); | ||
|
|
||
| var json = original.ToJson(); | ||
| Assert.False(string.IsNullOrEmpty(json)); | ||
|
|
||
| var reloaded = StreamFailure.FromJson(json); | ||
| Assert.NotNull(reloaded); | ||
| Assert.Equal(StreamFailureOutcome.Indeterminate, reloaded.Outcome); | ||
| Assert.Equal("SSE stream error: connection reset", reloaded.Message); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void RoundtripYaml() | ||
| { | ||
| // Test that FromYaml -> ToYaml -> FromYaml produces equivalent data | ||
| string yamlData = """ | ||
| outcome: indeterminate | ||
| message: "SSE stream error: connection reset" | ||
|
|
||
| """; | ||
|
|
||
| var original = StreamFailure.FromYaml(yamlData); | ||
| Assert.NotNull(original); | ||
|
|
||
| var yaml = original.ToYaml(); | ||
| Assert.False(string.IsNullOrEmpty(yaml)); | ||
|
|
||
| var reloaded = StreamFailure.FromYaml(yaml); | ||
| Assert.NotNull(reloaded); | ||
| Assert.Equal(StreamFailureOutcome.Indeterminate, reloaded.Outcome); | ||
| Assert.Equal("SSE stream error: connection reset", reloaded.Message); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void ToJsonProducesValidJson() | ||
| { | ||
| string jsonData = """ | ||
| { | ||
| "outcome": "indeterminate", | ||
| "message": "SSE stream error: connection reset" | ||
| } | ||
| """; | ||
|
|
||
| var instance = StreamFailure.FromJson(jsonData); | ||
| var json = instance.ToJson(); | ||
|
|
||
| // Verify it's valid JSON by parsing it | ||
| var parsed = System.Text.Json.JsonDocument.Parse(json); | ||
| Assert.NotNull(parsed); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void ToYamlProducesValidYaml() | ||
| { | ||
| string yamlData = """ | ||
| outcome: indeterminate | ||
| message: "SSE stream error: connection reset" | ||
|
|
||
| """; | ||
|
|
||
| var instance = StreamFailure.FromYaml(yamlData); | ||
| var yaml = instance.ToYaml(); | ||
|
|
||
| // Verify it's valid YAML by parsing it | ||
| var deserializer = new YamlDotNet.Serialization.DeserializerBuilder().Build(); | ||
| var parsed = deserializer.Deserialize<object>(yaml); | ||
| Assert.NotNull(parsed); | ||
| } | ||
| } |
165 changes: 165 additions & 0 deletions
165
runtime/csharp/Prompty.Core/Model/events/FailureChunk.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,165 @@ | ||
| // <auto-generated by typra-emitter> | ||
| // Copyright (c) Microsoft. All rights reserved. | ||
| using System.Text.Json; | ||
| using YamlDotNet.Serialization; | ||
|
|
||
| #pragma warning disable IDE0130 | ||
| namespace Prompty.Core; | ||
| #pragma warning restore IDE0130 | ||
|
|
||
| /// <summary> | ||
| /// A classified failure chunk from the LLM response stream. | ||
| /// </summary> | ||
| public partial class FailureChunk : StreamChunk | ||
| { | ||
| /// <summary> | ||
| /// The shorthand property name for this type, if any. | ||
| /// </summary> | ||
| public new static string? ShorthandProperty => null; | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of <see cref="FailureChunk"/>. | ||
| /// </summary> | ||
| #pragma warning disable CS8618 | ||
| public FailureChunk() | ||
| { | ||
| } | ||
| #pragma warning restore CS8618 | ||
|
|
||
| /// <summary> | ||
| /// The kind identifier for classified failure chunks | ||
| /// </summary> | ||
| public override string Kind { get; set; } = "failure"; | ||
|
|
||
| /// <summary> | ||
| /// The classified stream failure | ||
| /// </summary> | ||
| public StreamFailure Failure { get; set; } | ||
|
|
||
|
|
||
|
|
||
| #region Load Methods | ||
|
|
||
| /// <summary> | ||
| /// Load a FailureChunk instance from a dictionary. | ||
| /// </summary> | ||
| /// <param name="data">The dictionary containing the data.</param> | ||
| /// <param name="context">Optional context with pre/post processing callbacks.</param> | ||
| /// <returns>The loaded FailureChunk instance.</returns> | ||
| public new static FailureChunk Load(Dictionary<string, object?> data, LoadContext? context = null) | ||
| { | ||
| if (context is not null) | ||
| { | ||
| data = context.ProcessInput(data); | ||
| } | ||
|
|
||
|
|
||
| // Create new instance | ||
| var instance = new FailureChunk(); | ||
|
|
||
|
|
||
| if (data.TryGetValue("kind", out var kindValue) && kindValue is not null) | ||
| { | ||
| instance.Kind = kindValue?.ToString()!; | ||
| } | ||
|
|
||
| if (data.TryGetValue("failure", out var failureValue) && failureValue is not null) | ||
| { | ||
| instance.Failure = StreamFailure.Load(failureValue.GetDictionary(StreamFailure.ShorthandProperty), context); | ||
| } | ||
|
|
||
| if (context is not null) | ||
| { | ||
| instance = context.ProcessOutput(instance); | ||
| } | ||
| return instance; | ||
| } | ||
|
|
||
|
|
||
| #endregion | ||
|
|
||
| #region Save Methods | ||
|
|
||
| /// <summary> | ||
| /// Save the FailureChunk instance to a dictionary. | ||
| /// </summary> | ||
| /// <param name="context">Optional context with pre/post processing callbacks.</param> | ||
| /// <returns>The dictionary representation of this instance.</returns> | ||
| public override Dictionary<string, object?> Save(SaveContext? context = null) | ||
| { | ||
| var obj = this; | ||
| if (context is not null) | ||
| { | ||
| obj = context.ProcessObject(obj); | ||
| } | ||
|
|
||
|
|
||
| // Start with parent class properties | ||
| var result = base.Save(context); | ||
|
|
||
|
|
||
| result["kind"] = obj.Kind; | ||
|
|
||
|
|
||
| result["failure"] = obj.Failure?.Save(context); | ||
|
|
||
|
|
||
| return result; | ||
| } | ||
|
|
||
|
|
||
| /// <summary> | ||
| /// Convert the FailureChunk instance to a YAML string. | ||
| /// </summary> | ||
| /// <param name="context">Optional context with pre/post processing callbacks.</param> | ||
| /// <returns>The YAML string representation of this instance.</returns> | ||
| public new string ToYaml(SaveContext? context = null) | ||
| { | ||
| context ??= new SaveContext(); | ||
| return context.ToYaml(Save(context)); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Convert the FailureChunk instance to a JSON string. | ||
| /// </summary> | ||
| /// <param name="context">Optional context with pre/post processing callbacks.</param> | ||
| /// <param name="indent">Whether to indent the output. Defaults to true.</param> | ||
| /// <returns>The JSON string representation of this instance.</returns> | ||
| public new string ToJson(SaveContext? context = null, bool indent = true) | ||
| { | ||
| context ??= new SaveContext(); | ||
| return context.ToJson(Save(context), indent); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Load a FailureChunk instance from a JSON string. | ||
| /// </summary> | ||
| /// <param name="json">The JSON string to parse.</param> | ||
| /// <param name="context">Optional context with pre/post processing callbacks.</param> | ||
| /// <returns>The loaded FailureChunk instance.</returns> | ||
| public new static FailureChunk FromJson(string json, LoadContext? context = null) | ||
| { | ||
| using var doc = JsonDocument.Parse(json); | ||
| Dictionary<string, object?> dict; | ||
| dict = JsonSerializer.Deserialize<Dictionary<string, object?>>(json, JsonUtils.Options) | ||
| ?? throw new ArgumentException("Failed to parse JSON as dictionary"); | ||
|
|
||
| return Load(dict, context); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Load a FailureChunk instance from a YAML string. | ||
| /// </summary> | ||
| /// <param name="yaml">The YAML string to parse.</param> | ||
| /// <param name="context">Optional context with pre/post processing callbacks.</param> | ||
| /// <returns>The loaded FailureChunk instance.</returns> | ||
| public new static FailureChunk FromYaml(string yaml, LoadContext? context = null) | ||
| { | ||
| var dict = YamlUtils.Deserializer.Deserialize<Dictionary<string, object?>>(yaml) | ||
| ?? throw new ArgumentException("Failed to parse YAML as dictionary"); | ||
|
|
||
| return Load(dict, context); | ||
| } | ||
|
|
||
| #endregion | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file is generated by Typra 0.4.2, and the same
?.ToString()!emitter pattern occurs broadly across the generated C# target (359 occurrences in 130 files). Hand-editing this one generated class would be overwritten by deterministic regeneration and would make this schema-only PR non-reproducible. The coordinated Typra 0.4.4 consolidation will update the emitter pin and regenerate the combined schema after this PR lands; CodeQL itself passes in this PR.