diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..d149b0ce --- /dev/null +++ b/.editorconfig @@ -0,0 +1,19 @@ +root = true + +[*] +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.cs] +indent_style = space +indent_size = 4 + +[*.{csproj,sln}] +indent_style = space +indent_size = 2 + +[*.{json,yaml,yml}] +indent_style = space +indent_size = 2 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..1d338e0f --- /dev/null +++ b/.gitattributes @@ -0,0 +1,64 @@ +# Normalize all text files to LF in the repository. +# This eliminates CRLF drift on Windows (core.autocrlf=true) so that +# `npm run generate` never produces spurious line-ending diffs. + +# Default: auto-detect text files and normalize to LF on checkout. +* text=auto eol=lf + +# --- Source code --- +*.py text eol=lf +*.ts text eol=lf +*.tsx text eol=lf +*.js text eol=lf +*.mjs text eol=lf +*.rs text eol=lf +*.go text eol=lf +*.cs text eol=lf +*.tsp text eol=lf +*.astro text eol=lf +*.css text eol=lf +*.html text eol=lf + +# --- Config / data --- +*.json text eol=lf +*.yaml text eol=lf +*.yml text eol=lf +*.toml text eol=lf +*.md text eol=lf +*.mdx text eol=lf +*.txt text eol=lf +*.prompty text eol=lf +*.cfg text eol=lf +*.conf text eol=lf +*.mod text eol=lf +*.sum text eol=lf + +# --- Project files --- +*.csproj text eol=lf +*.sln text eol=lf +*.ps1 text eol=lf +*.bat text eol=lf + +# --- Dotfiles (no extension — treat as text) --- +.gitignore text eol=lf +.gitattributes text eol=lf +.dockerignore text eol=lf +.vscodeignore text eol=lf +.babelrc text eol=lf +.example text eol=lf +Dockerfile text eol=lf + +# --- Truly binary files --- keep as-is +*.png binary +*.exe binary +*.svg text eol=lf + +# --- Lock files: text but don't diff --- +*.lock text eol=lf -diff +package-lock.json text eol=lf -diff +Cargo.lock text eol=lf -diff + +# --- Generated marker (no content) --- +*.typed text eol=lf +*.tsbuildinfo text eol=lf +*.tracy text eol=lf diff --git a/runtime/csharp/Prompty.Core.Tests/Model/core/FileNotFoundErrorConversionTests.cs b/runtime/csharp/Prompty.Core.Tests/Model/core/FileNotFoundErrorConversionTests.cs new file mode 100644 index 00000000..6e7b73a9 --- /dev/null +++ b/runtime/csharp/Prompty.Core.Tests/Model/core/FileNotFoundErrorConversionTests.cs @@ -0,0 +1,122 @@ +using Xunit; + +#pragma warning disable IDE0130 +namespace Prompty.Core; +#pragma warning restore IDE0130 + + +public class FileNotFoundErrorConversionTests +{ + [Fact] + public void LoadYamlInput() + { + string yamlData = """ +message: "Prompty file not found: ./chat.prompty" +path: ./chat.prompty + +"""; + + var instance = FileNotFoundError.FromYaml(yamlData); + + Assert.NotNull(instance); + Assert.Equal("Prompty file not found: ./chat.prompty", instance.Message); + Assert.Equal("./chat.prompty", instance.Path); + } + + [Fact] + public void LoadJsonInput() + { + string jsonData = """ +{ + "message": "Prompty file not found: ./chat.prompty", + "path": "./chat.prompty" +} +"""; + + var instance = FileNotFoundError.FromJson(jsonData); + Assert.NotNull(instance); + Assert.Equal("Prompty file not found: ./chat.prompty", instance.Message); + Assert.Equal("./chat.prompty", instance.Path); + } + + [Fact] + public void RoundtripJson() + { + // Test that FromJson -> ToJson -> FromJson produces equivalent data + string jsonData = """ +{ + "message": "Prompty file not found: ./chat.prompty", + "path": "./chat.prompty" +} +"""; + + var original = FileNotFoundError.FromJson(jsonData); + Assert.NotNull(original); + + var json = original.ToJson(); + Assert.False(string.IsNullOrEmpty(json)); + + var reloaded = FileNotFoundError.FromJson(json); + Assert.NotNull(reloaded); + Assert.Equal("Prompty file not found: ./chat.prompty", reloaded.Message); + Assert.Equal("./chat.prompty", reloaded.Path); + } + + [Fact] + public void RoundtripYaml() + { + // Test that FromYaml -> ToYaml -> FromYaml produces equivalent data + string yamlData = """ +message: "Prompty file not found: ./chat.prompty" +path: ./chat.prompty + +"""; + + var original = FileNotFoundError.FromYaml(yamlData); + Assert.NotNull(original); + + var yaml = original.ToYaml(); + Assert.False(string.IsNullOrEmpty(yaml)); + + var reloaded = FileNotFoundError.FromYaml(yaml); + Assert.NotNull(reloaded); + Assert.Equal("Prompty file not found: ./chat.prompty", reloaded.Message); + Assert.Equal("./chat.prompty", reloaded.Path); + } + + [Fact] + public void ToJsonProducesValidJson() + { + string jsonData = """ +{ + "message": "Prompty file not found: ./chat.prompty", + "path": "./chat.prompty" +} +"""; + + var instance = FileNotFoundError.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 = """ +message: "Prompty file not found: ./chat.prompty" +path: ./chat.prompty + +"""; + + var instance = FileNotFoundError.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(yaml); + Assert.NotNull(parsed); + } +} diff --git a/runtime/csharp/Prompty.Core.Tests/Model/core/InvokerErrorConversionTests.cs b/runtime/csharp/Prompty.Core.Tests/Model/core/InvokerErrorConversionTests.cs new file mode 100644 index 00000000..f57d39f7 --- /dev/null +++ b/runtime/csharp/Prompty.Core.Tests/Model/core/InvokerErrorConversionTests.cs @@ -0,0 +1,132 @@ +using Xunit; + +#pragma warning disable IDE0130 +namespace Prompty.Core; +#pragma warning restore IDE0130 + + +public class InvokerErrorConversionTests +{ + [Fact] + public void LoadYamlInput() + { + string yamlData = """ +message: "No renderer registered for key: jinja2" +component: renderer +key: jinja2 + +"""; + + var instance = InvokerError.FromYaml(yamlData); + + Assert.NotNull(instance); + Assert.Equal("No renderer registered for key: jinja2", instance.Message); + Assert.Equal("renderer", instance.Component); + Assert.Equal("jinja2", instance.Key); + } + + [Fact] + public void LoadJsonInput() + { + string jsonData = """ +{ + "message": "No renderer registered for key: jinja2", + "component": "renderer", + "key": "jinja2" +} +"""; + + var instance = InvokerError.FromJson(jsonData); + Assert.NotNull(instance); + Assert.Equal("No renderer registered for key: jinja2", instance.Message); + Assert.Equal("renderer", instance.Component); + Assert.Equal("jinja2", instance.Key); + } + + [Fact] + public void RoundtripJson() + { + // Test that FromJson -> ToJson -> FromJson produces equivalent data + string jsonData = """ +{ + "message": "No renderer registered for key: jinja2", + "component": "renderer", + "key": "jinja2" +} +"""; + + var original = InvokerError.FromJson(jsonData); + Assert.NotNull(original); + + var json = original.ToJson(); + Assert.False(string.IsNullOrEmpty(json)); + + var reloaded = InvokerError.FromJson(json); + Assert.NotNull(reloaded); + Assert.Equal("No renderer registered for key: jinja2", reloaded.Message); + Assert.Equal("renderer", reloaded.Component); + Assert.Equal("jinja2", reloaded.Key); + } + + [Fact] + public void RoundtripYaml() + { + // Test that FromYaml -> ToYaml -> FromYaml produces equivalent data + string yamlData = """ +message: "No renderer registered for key: jinja2" +component: renderer +key: jinja2 + +"""; + + var original = InvokerError.FromYaml(yamlData); + Assert.NotNull(original); + + var yaml = original.ToYaml(); + Assert.False(string.IsNullOrEmpty(yaml)); + + var reloaded = InvokerError.FromYaml(yaml); + Assert.NotNull(reloaded); + Assert.Equal("No renderer registered for key: jinja2", reloaded.Message); + Assert.Equal("renderer", reloaded.Component); + Assert.Equal("jinja2", reloaded.Key); + } + + [Fact] + public void ToJsonProducesValidJson() + { + string jsonData = """ +{ + "message": "No renderer registered for key: jinja2", + "component": "renderer", + "key": "jinja2" +} +"""; + + var instance = InvokerError.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 = """ +message: "No renderer registered for key: jinja2" +component: renderer +key: jinja2 + +"""; + + var instance = InvokerError.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(yaml); + Assert.NotNull(parsed); + } +} diff --git a/runtime/csharp/Prompty.Core.Tests/Model/core/ValidationErrorConversionTests.cs b/runtime/csharp/Prompty.Core.Tests/Model/core/ValidationErrorConversionTests.cs new file mode 100644 index 00000000..99d9e33b --- /dev/null +++ b/runtime/csharp/Prompty.Core.Tests/Model/core/ValidationErrorConversionTests.cs @@ -0,0 +1,132 @@ +using Xunit; + +#pragma warning disable IDE0130 +namespace Prompty.Core; +#pragma warning restore IDE0130 + + +public class ValidationErrorConversionTests +{ + [Fact] + public void LoadYamlInput() + { + string yamlData = """ +message: "Missing required input: firstName" +property: firstName +constraint: required + +"""; + + var instance = ValidationError.FromYaml(yamlData); + + Assert.NotNull(instance); + Assert.Equal("Missing required input: firstName", instance.Message); + Assert.Equal("firstName", instance.Property); + Assert.Equal("required", instance.Constraint); + } + + [Fact] + public void LoadJsonInput() + { + string jsonData = """ +{ + "message": "Missing required input: firstName", + "property": "firstName", + "constraint": "required" +} +"""; + + var instance = ValidationError.FromJson(jsonData); + Assert.NotNull(instance); + Assert.Equal("Missing required input: firstName", instance.Message); + Assert.Equal("firstName", instance.Property); + Assert.Equal("required", instance.Constraint); + } + + [Fact] + public void RoundtripJson() + { + // Test that FromJson -> ToJson -> FromJson produces equivalent data + string jsonData = """ +{ + "message": "Missing required input: firstName", + "property": "firstName", + "constraint": "required" +} +"""; + + var original = ValidationError.FromJson(jsonData); + Assert.NotNull(original); + + var json = original.ToJson(); + Assert.False(string.IsNullOrEmpty(json)); + + var reloaded = ValidationError.FromJson(json); + Assert.NotNull(reloaded); + Assert.Equal("Missing required input: firstName", reloaded.Message); + Assert.Equal("firstName", reloaded.Property); + Assert.Equal("required", reloaded.Constraint); + } + + [Fact] + public void RoundtripYaml() + { + // Test that FromYaml -> ToYaml -> FromYaml produces equivalent data + string yamlData = """ +message: "Missing required input: firstName" +property: firstName +constraint: required + +"""; + + var original = ValidationError.FromYaml(yamlData); + Assert.NotNull(original); + + var yaml = original.ToYaml(); + Assert.False(string.IsNullOrEmpty(yaml)); + + var reloaded = ValidationError.FromYaml(yaml); + Assert.NotNull(reloaded); + Assert.Equal("Missing required input: firstName", reloaded.Message); + Assert.Equal("firstName", reloaded.Property); + Assert.Equal("required", reloaded.Constraint); + } + + [Fact] + public void ToJsonProducesValidJson() + { + string jsonData = """ +{ + "message": "Missing required input: firstName", + "property": "firstName", + "constraint": "required" +} +"""; + + var instance = ValidationError.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 = """ +message: "Missing required input: firstName" +property: firstName +constraint: required + +"""; + + var instance = ValidationError.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(yaml); + Assert.NotNull(parsed); + } +} diff --git a/runtime/csharp/Prompty.Core.Tests/Model/core/ValidationResultConversionTests.cs b/runtime/csharp/Prompty.Core.Tests/Model/core/ValidationResultConversionTests.cs new file mode 100644 index 00000000..f8157812 --- /dev/null +++ b/runtime/csharp/Prompty.Core.Tests/Model/core/ValidationResultConversionTests.cs @@ -0,0 +1,118 @@ +using Xunit; + +#pragma warning disable IDE0130 +namespace Prompty.Core; +#pragma warning restore IDE0130 + + +public class ValidationResultConversionTests +{ + [Fact] + public void LoadYamlInput() + { + string yamlData = """ +valid: true +errors: [] + +"""; + + var instance = ValidationResult.FromYaml(yamlData); + + Assert.NotNull(instance); + Assert.True(instance.Valid); + } + + [Fact] + public void LoadJsonInput() + { + string jsonData = """ +{ + "valid": true, + "errors": [] +} +"""; + + var instance = ValidationResult.FromJson(jsonData); + Assert.NotNull(instance); + Assert.True(instance.Valid); + } + + [Fact] + public void RoundtripJson() + { + // Test that FromJson -> ToJson -> FromJson produces equivalent data + string jsonData = """ +{ + "valid": true, + "errors": [] +} +"""; + + var original = ValidationResult.FromJson(jsonData); + Assert.NotNull(original); + + var json = original.ToJson(); + Assert.False(string.IsNullOrEmpty(json)); + + var reloaded = ValidationResult.FromJson(json); + Assert.NotNull(reloaded); + Assert.True(reloaded.Valid); + } + + [Fact] + public void RoundtripYaml() + { + // Test that FromYaml -> ToYaml -> FromYaml produces equivalent data + string yamlData = """ +valid: true +errors: [] + +"""; + + var original = ValidationResult.FromYaml(yamlData); + Assert.NotNull(original); + + var yaml = original.ToYaml(); + Assert.False(string.IsNullOrEmpty(yaml)); + + var reloaded = ValidationResult.FromYaml(yaml); + Assert.NotNull(reloaded); + Assert.True(reloaded.Valid); + } + + [Fact] + public void ToJsonProducesValidJson() + { + string jsonData = """ +{ + "valid": true, + "errors": [] +} +"""; + + var instance = ValidationResult.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 = """ +valid: true +errors: [] + +"""; + + var instance = ValidationResult.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(yaml); + Assert.NotNull(parsed); + } +} diff --git a/runtime/csharp/Prompty.Core.Tests/Model/streaming/StreamOptionsConversionTests.cs b/runtime/csharp/Prompty.Core.Tests/Model/streaming/StreamOptionsConversionTests.cs new file mode 100644 index 00000000..ddf23e47 --- /dev/null +++ b/runtime/csharp/Prompty.Core.Tests/Model/streaming/StreamOptionsConversionTests.cs @@ -0,0 +1,112 @@ +using Xunit; + +#pragma warning disable IDE0130 +namespace Prompty.Core; +#pragma warning restore IDE0130 + + +public class StreamOptionsConversionTests +{ + [Fact] + public void LoadYamlInput() + { + string yamlData = """ +includeUsage: true + +"""; + + var instance = StreamOptions.FromYaml(yamlData); + + Assert.NotNull(instance); + Assert.True(instance.IncludeUsage); + } + + [Fact] + public void LoadJsonInput() + { + string jsonData = """ +{ + "includeUsage": true +} +"""; + + var instance = StreamOptions.FromJson(jsonData); + Assert.NotNull(instance); + Assert.True(instance.IncludeUsage); + } + + [Fact] + public void RoundtripJson() + { + // Test that FromJson -> ToJson -> FromJson produces equivalent data + string jsonData = """ +{ + "includeUsage": true +} +"""; + + var original = StreamOptions.FromJson(jsonData); + Assert.NotNull(original); + + var json = original.ToJson(); + Assert.False(string.IsNullOrEmpty(json)); + + var reloaded = StreamOptions.FromJson(json); + Assert.NotNull(reloaded); + Assert.True(reloaded.IncludeUsage); + } + + [Fact] + public void RoundtripYaml() + { + // Test that FromYaml -> ToYaml -> FromYaml produces equivalent data + string yamlData = """ +includeUsage: true + +"""; + + var original = StreamOptions.FromYaml(yamlData); + Assert.NotNull(original); + + var yaml = original.ToYaml(); + Assert.False(string.IsNullOrEmpty(yaml)); + + var reloaded = StreamOptions.FromYaml(yaml); + Assert.NotNull(reloaded); + Assert.True(reloaded.IncludeUsage); + } + + [Fact] + public void ToJsonProducesValidJson() + { + string jsonData = """ +{ + "includeUsage": true +} +"""; + + var instance = StreamOptions.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 = """ +includeUsage: true + +"""; + + var instance = StreamOptions.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(yaml); + Assert.NotNull(parsed); + } +} diff --git a/runtime/csharp/Prompty.Core.Tests/Model/tracing/TraceFileConversionTests.cs b/runtime/csharp/Prompty.Core.Tests/Model/tracing/TraceFileConversionTests.cs new file mode 100644 index 00000000..67ae4d98 --- /dev/null +++ b/runtime/csharp/Prompty.Core.Tests/Model/tracing/TraceFileConversionTests.cs @@ -0,0 +1,122 @@ +using Xunit; + +#pragma warning disable IDE0130 +namespace Prompty.Core; +#pragma warning restore IDE0130 + + +public class TraceFileConversionTests +{ + [Fact] + public void LoadYamlInput() + { + string yamlData = """ +runtime: python +version: 2.0.0 + +"""; + + var instance = TraceFile.FromYaml(yamlData); + + Assert.NotNull(instance); + Assert.Equal("python", instance.Runtime); + Assert.Equal("2.0.0", instance.Version); + } + + [Fact] + public void LoadJsonInput() + { + string jsonData = """ +{ + "runtime": "python", + "version": "2.0.0" +} +"""; + + var instance = TraceFile.FromJson(jsonData); + Assert.NotNull(instance); + Assert.Equal("python", instance.Runtime); + Assert.Equal("2.0.0", instance.Version); + } + + [Fact] + public void RoundtripJson() + { + // Test that FromJson -> ToJson -> FromJson produces equivalent data + string jsonData = """ +{ + "runtime": "python", + "version": "2.0.0" +} +"""; + + var original = TraceFile.FromJson(jsonData); + Assert.NotNull(original); + + var json = original.ToJson(); + Assert.False(string.IsNullOrEmpty(json)); + + var reloaded = TraceFile.FromJson(json); + Assert.NotNull(reloaded); + Assert.Equal("python", reloaded.Runtime); + Assert.Equal("2.0.0", reloaded.Version); + } + + [Fact] + public void RoundtripYaml() + { + // Test that FromYaml -> ToYaml -> FromYaml produces equivalent data + string yamlData = """ +runtime: python +version: 2.0.0 + +"""; + + var original = TraceFile.FromYaml(yamlData); + Assert.NotNull(original); + + var yaml = original.ToYaml(); + Assert.False(string.IsNullOrEmpty(yaml)); + + var reloaded = TraceFile.FromYaml(yaml); + Assert.NotNull(reloaded); + Assert.Equal("python", reloaded.Runtime); + Assert.Equal("2.0.0", reloaded.Version); + } + + [Fact] + public void ToJsonProducesValidJson() + { + string jsonData = """ +{ + "runtime": "python", + "version": "2.0.0" +} +"""; + + var instance = TraceFile.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 = """ +runtime: python +version: 2.0.0 + +"""; + + var instance = TraceFile.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(yaml); + Assert.NotNull(parsed); + } +} diff --git a/runtime/csharp/Prompty.Core.Tests/Model/tracing/TraceSpanConversionTests.cs b/runtime/csharp/Prompty.Core.Tests/Model/tracing/TraceSpanConversionTests.cs new file mode 100644 index 00000000..23b18fca --- /dev/null +++ b/runtime/csharp/Prompty.Core.Tests/Model/tracing/TraceSpanConversionTests.cs @@ -0,0 +1,132 @@ +using Xunit; + +#pragma warning disable IDE0130 +namespace Prompty.Core; +#pragma warning restore IDE0130 + + +public class TraceSpanConversionTests +{ + [Fact] + public void LoadYamlInput() + { + string yamlData = """ +name: prompty.core.pipeline.run +signature: prompty.core.pipeline.run +error: Connection refused + +"""; + + var instance = TraceSpan.FromYaml(yamlData); + + Assert.NotNull(instance); + Assert.Equal("prompty.core.pipeline.run", instance.Name); + Assert.Equal("prompty.core.pipeline.run", instance.Signature); + Assert.Equal("Connection refused", instance.Error); + } + + [Fact] + public void LoadJsonInput() + { + string jsonData = """ +{ + "name": "prompty.core.pipeline.run", + "signature": "prompty.core.pipeline.run", + "error": "Connection refused" +} +"""; + + var instance = TraceSpan.FromJson(jsonData); + Assert.NotNull(instance); + Assert.Equal("prompty.core.pipeline.run", instance.Name); + Assert.Equal("prompty.core.pipeline.run", instance.Signature); + Assert.Equal("Connection refused", instance.Error); + } + + [Fact] + public void RoundtripJson() + { + // Test that FromJson -> ToJson -> FromJson produces equivalent data + string jsonData = """ +{ + "name": "prompty.core.pipeline.run", + "signature": "prompty.core.pipeline.run", + "error": "Connection refused" +} +"""; + + var original = TraceSpan.FromJson(jsonData); + Assert.NotNull(original); + + var json = original.ToJson(); + Assert.False(string.IsNullOrEmpty(json)); + + var reloaded = TraceSpan.FromJson(json); + Assert.NotNull(reloaded); + Assert.Equal("prompty.core.pipeline.run", reloaded.Name); + Assert.Equal("prompty.core.pipeline.run", reloaded.Signature); + Assert.Equal("Connection refused", reloaded.Error); + } + + [Fact] + public void RoundtripYaml() + { + // Test that FromYaml -> ToYaml -> FromYaml produces equivalent data + string yamlData = """ +name: prompty.core.pipeline.run +signature: prompty.core.pipeline.run +error: Connection refused + +"""; + + var original = TraceSpan.FromYaml(yamlData); + Assert.NotNull(original); + + var yaml = original.ToYaml(); + Assert.False(string.IsNullOrEmpty(yaml)); + + var reloaded = TraceSpan.FromYaml(yaml); + Assert.NotNull(reloaded); + Assert.Equal("prompty.core.pipeline.run", reloaded.Name); + Assert.Equal("prompty.core.pipeline.run", reloaded.Signature); + Assert.Equal("Connection refused", reloaded.Error); + } + + [Fact] + public void ToJsonProducesValidJson() + { + string jsonData = """ +{ + "name": "prompty.core.pipeline.run", + "signature": "prompty.core.pipeline.run", + "error": "Connection refused" +} +"""; + + var instance = TraceSpan.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 = """ +name: prompty.core.pipeline.run +signature: prompty.core.pipeline.run +error: Connection refused + +"""; + + var instance = TraceSpan.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(yaml); + Assert.NotNull(parsed); + } +} diff --git a/runtime/csharp/Prompty.Core.Tests/Model/tracing/TraceTimeConversionTests.cs b/runtime/csharp/Prompty.Core.Tests/Model/tracing/TraceTimeConversionTests.cs new file mode 100644 index 00000000..00cbd26e --- /dev/null +++ b/runtime/csharp/Prompty.Core.Tests/Model/tracing/TraceTimeConversionTests.cs @@ -0,0 +1,132 @@ +using Xunit; + +#pragma warning disable IDE0130 +namespace Prompty.Core; +#pragma warning restore IDE0130 + + +public class TraceTimeConversionTests +{ + [Fact] + public void LoadYamlInput() + { + string yamlData = """ +start: "2026-04-04T12:00:00Z" +end: "2026-04-04T12:00:01Z" +duration: 1000 + +"""; + + var instance = TraceTime.FromYaml(yamlData); + + Assert.NotNull(instance); + Assert.Equal("2026-04-04T12:00:00Z", instance.Start); + Assert.Equal("2026-04-04T12:00:01Z", instance.End); + Assert.Equal(1000, instance.Duration); + } + + [Fact] + public void LoadJsonInput() + { + string jsonData = """ +{ + "start": "2026-04-04T12:00:00Z", + "end": "2026-04-04T12:00:01Z", + "duration": 1000 +} +"""; + + var instance = TraceTime.FromJson(jsonData); + Assert.NotNull(instance); + Assert.Equal("2026-04-04T12:00:00Z", instance.Start); + Assert.Equal("2026-04-04T12:00:01Z", instance.End); + Assert.Equal(1000, instance.Duration); + } + + [Fact] + public void RoundtripJson() + { + // Test that FromJson -> ToJson -> FromJson produces equivalent data + string jsonData = """ +{ + "start": "2026-04-04T12:00:00Z", + "end": "2026-04-04T12:00:01Z", + "duration": 1000 +} +"""; + + var original = TraceTime.FromJson(jsonData); + Assert.NotNull(original); + + var json = original.ToJson(); + Assert.False(string.IsNullOrEmpty(json)); + + var reloaded = TraceTime.FromJson(json); + Assert.NotNull(reloaded); + Assert.Equal("2026-04-04T12:00:00Z", reloaded.Start); + Assert.Equal("2026-04-04T12:00:01Z", reloaded.End); + Assert.Equal(1000, reloaded.Duration); + } + + [Fact] + public void RoundtripYaml() + { + // Test that FromYaml -> ToYaml -> FromYaml produces equivalent data + string yamlData = """ +start: "2026-04-04T12:00:00Z" +end: "2026-04-04T12:00:01Z" +duration: 1000 + +"""; + + var original = TraceTime.FromYaml(yamlData); + Assert.NotNull(original); + + var yaml = original.ToYaml(); + Assert.False(string.IsNullOrEmpty(yaml)); + + var reloaded = TraceTime.FromYaml(yaml); + Assert.NotNull(reloaded); + Assert.Equal("2026-04-04T12:00:00Z", reloaded.Start); + Assert.Equal("2026-04-04T12:00:01Z", reloaded.End); + Assert.Equal(1000, reloaded.Duration); + } + + [Fact] + public void ToJsonProducesValidJson() + { + string jsonData = """ +{ + "start": "2026-04-04T12:00:00Z", + "end": "2026-04-04T12:00:01Z", + "duration": 1000 +} +"""; + + var instance = TraceTime.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 = """ +start: "2026-04-04T12:00:00Z" +end: "2026-04-04T12:00:01Z" +duration: 1000 + +"""; + + var instance = TraceTime.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(yaml); + Assert.NotNull(parsed); + } +} diff --git a/runtime/csharp/Prompty.Core.Tests/Model/wire/AnthropicImageBlockConversionTests.cs b/runtime/csharp/Prompty.Core.Tests/Model/wire/AnthropicImageBlockConversionTests.cs new file mode 100644 index 00000000..dee7235c --- /dev/null +++ b/runtime/csharp/Prompty.Core.Tests/Model/wire/AnthropicImageBlockConversionTests.cs @@ -0,0 +1,10 @@ +using Xunit; + +#pragma warning disable IDE0130 +namespace Prompty.Core; +#pragma warning restore IDE0130 + + +public class AnthropicImageBlockConversionTests +{ +} diff --git a/runtime/csharp/Prompty.Core.Tests/Model/wire/AnthropicImageSourceConversionTests.cs b/runtime/csharp/Prompty.Core.Tests/Model/wire/AnthropicImageSourceConversionTests.cs new file mode 100644 index 00000000..c5cedd30 --- /dev/null +++ b/runtime/csharp/Prompty.Core.Tests/Model/wire/AnthropicImageSourceConversionTests.cs @@ -0,0 +1,122 @@ +using Xunit; + +#pragma warning disable IDE0130 +namespace Prompty.Core; +#pragma warning restore IDE0130 + + +public class AnthropicImageSourceConversionTests +{ + [Fact] + public void LoadYamlInput() + { + string yamlData = """ +media_type: image/png +data: iVBORw0KGgo... + +"""; + + var instance = AnthropicImageSource.FromYaml(yamlData); + + Assert.NotNull(instance); + Assert.Equal("image/png", instance.MediaType); + Assert.Equal("iVBORw0KGgo...", instance.Data); + } + + [Fact] + public void LoadJsonInput() + { + string jsonData = """ +{ + "media_type": "image/png", + "data": "iVBORw0KGgo..." +} +"""; + + var instance = AnthropicImageSource.FromJson(jsonData); + Assert.NotNull(instance); + Assert.Equal("image/png", instance.MediaType); + Assert.Equal("iVBORw0KGgo...", instance.Data); + } + + [Fact] + public void RoundtripJson() + { + // Test that FromJson -> ToJson -> FromJson produces equivalent data + string jsonData = """ +{ + "media_type": "image/png", + "data": "iVBORw0KGgo..." +} +"""; + + var original = AnthropicImageSource.FromJson(jsonData); + Assert.NotNull(original); + + var json = original.ToJson(); + Assert.False(string.IsNullOrEmpty(json)); + + var reloaded = AnthropicImageSource.FromJson(json); + Assert.NotNull(reloaded); + Assert.Equal("image/png", reloaded.MediaType); + Assert.Equal("iVBORw0KGgo...", reloaded.Data); + } + + [Fact] + public void RoundtripYaml() + { + // Test that FromYaml -> ToYaml -> FromYaml produces equivalent data + string yamlData = """ +media_type: image/png +data: iVBORw0KGgo... + +"""; + + var original = AnthropicImageSource.FromYaml(yamlData); + Assert.NotNull(original); + + var yaml = original.ToYaml(); + Assert.False(string.IsNullOrEmpty(yaml)); + + var reloaded = AnthropicImageSource.FromYaml(yaml); + Assert.NotNull(reloaded); + Assert.Equal("image/png", reloaded.MediaType); + Assert.Equal("iVBORw0KGgo...", reloaded.Data); + } + + [Fact] + public void ToJsonProducesValidJson() + { + string jsonData = """ +{ + "media_type": "image/png", + "data": "iVBORw0KGgo..." +} +"""; + + var instance = AnthropicImageSource.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 = """ +media_type: image/png +data: iVBORw0KGgo... + +"""; + + var instance = AnthropicImageSource.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(yaml); + Assert.NotNull(parsed); + } +} diff --git a/runtime/csharp/Prompty.Core.Tests/Model/wire/AnthropicMessagesRequestConversionTests.cs b/runtime/csharp/Prompty.Core.Tests/Model/wire/AnthropicMessagesRequestConversionTests.cs new file mode 100644 index 00000000..a425a7d5 --- /dev/null +++ b/runtime/csharp/Prompty.Core.Tests/Model/wire/AnthropicMessagesRequestConversionTests.cs @@ -0,0 +1,177 @@ +using Xunit; + +#pragma warning disable IDE0130 +namespace Prompty.Core; +#pragma warning restore IDE0130 + + +public class AnthropicMessagesRequestConversionTests +{ + [Fact] + public void LoadYamlInput() + { + string yamlData = """ +model: claude-sonnet-4-20250514 +max_tokens: 4096 +system: You are a helpful assistant. +temperature: 0.7 +top_p: 0.9 +top_k: 40 +stop_sequences: + - "\n\nHuman:" + +"""; + + var instance = AnthropicMessagesRequest.FromYaml(yamlData); + + Assert.NotNull(instance); + Assert.Equal("claude-sonnet-4-20250514", instance.Model); + Assert.Equal(4096, instance.MaxTokens); + Assert.Equal("You are a helpful assistant.", instance.System); + Assert.Equal(0.7f, instance.Temperature); + Assert.Equal(0.9f, instance.TopP); + Assert.Equal(40, instance.TopK); + } + + [Fact] + public void LoadJsonInput() + { + string jsonData = """ +{ + "model": "claude-sonnet-4-20250514", + "max_tokens": 4096, + "system": "You are a helpful assistant.", + "temperature": 0.7, + "top_p": 0.9, + "top_k": 40, + "stop_sequences": [ + "\n\nHuman:" + ] +} +"""; + + var instance = AnthropicMessagesRequest.FromJson(jsonData); + Assert.NotNull(instance); + Assert.Equal("claude-sonnet-4-20250514", instance.Model); + Assert.Equal(4096, instance.MaxTokens); + Assert.Equal("You are a helpful assistant.", instance.System); + Assert.Equal(0.7f, instance.Temperature); + Assert.Equal(0.9f, instance.TopP); + Assert.Equal(40, instance.TopK); + } + + [Fact] + public void RoundtripJson() + { + // Test that FromJson -> ToJson -> FromJson produces equivalent data + string jsonData = """ +{ + "model": "claude-sonnet-4-20250514", + "max_tokens": 4096, + "system": "You are a helpful assistant.", + "temperature": 0.7, + "top_p": 0.9, + "top_k": 40, + "stop_sequences": [ + "\n\nHuman:" + ] +} +"""; + + var original = AnthropicMessagesRequest.FromJson(jsonData); + Assert.NotNull(original); + + var json = original.ToJson(); + Assert.False(string.IsNullOrEmpty(json)); + + var reloaded = AnthropicMessagesRequest.FromJson(json); + Assert.NotNull(reloaded); + Assert.Equal("claude-sonnet-4-20250514", reloaded.Model); + Assert.Equal(4096, reloaded.MaxTokens); + Assert.Equal("You are a helpful assistant.", reloaded.System); + Assert.Equal(0.7f, reloaded.Temperature); + Assert.Equal(0.9f, reloaded.TopP); + Assert.Equal(40, reloaded.TopK); + } + + [Fact] + public void RoundtripYaml() + { + // Test that FromYaml -> ToYaml -> FromYaml produces equivalent data + string yamlData = """ +model: claude-sonnet-4-20250514 +max_tokens: 4096 +system: You are a helpful assistant. +temperature: 0.7 +top_p: 0.9 +top_k: 40 +stop_sequences: + - "\n\nHuman:" + +"""; + + var original = AnthropicMessagesRequest.FromYaml(yamlData); + Assert.NotNull(original); + + var yaml = original.ToYaml(); + Assert.False(string.IsNullOrEmpty(yaml)); + + var reloaded = AnthropicMessagesRequest.FromYaml(yaml); + Assert.NotNull(reloaded); + Assert.Equal("claude-sonnet-4-20250514", reloaded.Model); + Assert.Equal(4096, reloaded.MaxTokens); + Assert.Equal("You are a helpful assistant.", reloaded.System); + Assert.Equal(0.7f, reloaded.Temperature); + Assert.Equal(0.9f, reloaded.TopP); + Assert.Equal(40, reloaded.TopK); + } + + [Fact] + public void ToJsonProducesValidJson() + { + string jsonData = """ +{ + "model": "claude-sonnet-4-20250514", + "max_tokens": 4096, + "system": "You are a helpful assistant.", + "temperature": 0.7, + "top_p": 0.9, + "top_k": 40, + "stop_sequences": [ + "\n\nHuman:" + ] +} +"""; + + var instance = AnthropicMessagesRequest.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 = """ +model: claude-sonnet-4-20250514 +max_tokens: 4096 +system: You are a helpful assistant. +temperature: 0.7 +top_p: 0.9 +top_k: 40 +stop_sequences: + - "\n\nHuman:" + +"""; + + var instance = AnthropicMessagesRequest.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(yaml); + Assert.NotNull(parsed); + } +} diff --git a/runtime/csharp/Prompty.Core.Tests/Model/wire/AnthropicMessagesResponseConversionTests.cs b/runtime/csharp/Prompty.Core.Tests/Model/wire/AnthropicMessagesResponseConversionTests.cs new file mode 100644 index 00000000..47d6e93a --- /dev/null +++ b/runtime/csharp/Prompty.Core.Tests/Model/wire/AnthropicMessagesResponseConversionTests.cs @@ -0,0 +1,132 @@ +using Xunit; + +#pragma warning disable IDE0130 +namespace Prompty.Core; +#pragma warning restore IDE0130 + + +public class AnthropicMessagesResponseConversionTests +{ + [Fact] + public void LoadYamlInput() + { + string yamlData = """ +id: msg_01XFDUDYJgAACzvnptvVoYEL +model: claude-sonnet-4-20250514 +stop_reason: end_turn + +"""; + + var instance = AnthropicMessagesResponse.FromYaml(yamlData); + + Assert.NotNull(instance); + Assert.Equal("msg_01XFDUDYJgAACzvnptvVoYEL", instance.Id); + Assert.Equal("claude-sonnet-4-20250514", instance.Model); + Assert.Equal("end_turn", instance.StopReason); + } + + [Fact] + public void LoadJsonInput() + { + string jsonData = """ +{ + "id": "msg_01XFDUDYJgAACzvnptvVoYEL", + "model": "claude-sonnet-4-20250514", + "stop_reason": "end_turn" +} +"""; + + var instance = AnthropicMessagesResponse.FromJson(jsonData); + Assert.NotNull(instance); + Assert.Equal("msg_01XFDUDYJgAACzvnptvVoYEL", instance.Id); + Assert.Equal("claude-sonnet-4-20250514", instance.Model); + Assert.Equal("end_turn", instance.StopReason); + } + + [Fact] + public void RoundtripJson() + { + // Test that FromJson -> ToJson -> FromJson produces equivalent data + string jsonData = """ +{ + "id": "msg_01XFDUDYJgAACzvnptvVoYEL", + "model": "claude-sonnet-4-20250514", + "stop_reason": "end_turn" +} +"""; + + var original = AnthropicMessagesResponse.FromJson(jsonData); + Assert.NotNull(original); + + var json = original.ToJson(); + Assert.False(string.IsNullOrEmpty(json)); + + var reloaded = AnthropicMessagesResponse.FromJson(json); + Assert.NotNull(reloaded); + Assert.Equal("msg_01XFDUDYJgAACzvnptvVoYEL", reloaded.Id); + Assert.Equal("claude-sonnet-4-20250514", reloaded.Model); + Assert.Equal("end_turn", reloaded.StopReason); + } + + [Fact] + public void RoundtripYaml() + { + // Test that FromYaml -> ToYaml -> FromYaml produces equivalent data + string yamlData = """ +id: msg_01XFDUDYJgAACzvnptvVoYEL +model: claude-sonnet-4-20250514 +stop_reason: end_turn + +"""; + + var original = AnthropicMessagesResponse.FromYaml(yamlData); + Assert.NotNull(original); + + var yaml = original.ToYaml(); + Assert.False(string.IsNullOrEmpty(yaml)); + + var reloaded = AnthropicMessagesResponse.FromYaml(yaml); + Assert.NotNull(reloaded); + Assert.Equal("msg_01XFDUDYJgAACzvnptvVoYEL", reloaded.Id); + Assert.Equal("claude-sonnet-4-20250514", reloaded.Model); + Assert.Equal("end_turn", reloaded.StopReason); + } + + [Fact] + public void ToJsonProducesValidJson() + { + string jsonData = """ +{ + "id": "msg_01XFDUDYJgAACzvnptvVoYEL", + "model": "claude-sonnet-4-20250514", + "stop_reason": "end_turn" +} +"""; + + var instance = AnthropicMessagesResponse.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 = """ +id: msg_01XFDUDYJgAACzvnptvVoYEL +model: claude-sonnet-4-20250514 +stop_reason: end_turn + +"""; + + var instance = AnthropicMessagesResponse.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(yaml); + Assert.NotNull(parsed); + } +} diff --git a/runtime/csharp/Prompty.Core.Tests/Model/wire/AnthropicTextBlockConversionTests.cs b/runtime/csharp/Prompty.Core.Tests/Model/wire/AnthropicTextBlockConversionTests.cs new file mode 100644 index 00000000..e516e507 --- /dev/null +++ b/runtime/csharp/Prompty.Core.Tests/Model/wire/AnthropicTextBlockConversionTests.cs @@ -0,0 +1,112 @@ +using Xunit; + +#pragma warning disable IDE0130 +namespace Prompty.Core; +#pragma warning restore IDE0130 + + +public class AnthropicTextBlockConversionTests +{ + [Fact] + public void LoadYamlInput() + { + string yamlData = """ +text: Hello, how can I help? + +"""; + + var instance = AnthropicTextBlock.FromYaml(yamlData); + + Assert.NotNull(instance); + Assert.Equal("Hello, how can I help?", instance.Text); + } + + [Fact] + public void LoadJsonInput() + { + string jsonData = """ +{ + "text": "Hello, how can I help?" +} +"""; + + var instance = AnthropicTextBlock.FromJson(jsonData); + Assert.NotNull(instance); + Assert.Equal("Hello, how can I help?", instance.Text); + } + + [Fact] + public void RoundtripJson() + { + // Test that FromJson -> ToJson -> FromJson produces equivalent data + string jsonData = """ +{ + "text": "Hello, how can I help?" +} +"""; + + var original = AnthropicTextBlock.FromJson(jsonData); + Assert.NotNull(original); + + var json = original.ToJson(); + Assert.False(string.IsNullOrEmpty(json)); + + var reloaded = AnthropicTextBlock.FromJson(json); + Assert.NotNull(reloaded); + Assert.Equal("Hello, how can I help?", reloaded.Text); + } + + [Fact] + public void RoundtripYaml() + { + // Test that FromYaml -> ToYaml -> FromYaml produces equivalent data + string yamlData = """ +text: Hello, how can I help? + +"""; + + var original = AnthropicTextBlock.FromYaml(yamlData); + Assert.NotNull(original); + + var yaml = original.ToYaml(); + Assert.False(string.IsNullOrEmpty(yaml)); + + var reloaded = AnthropicTextBlock.FromYaml(yaml); + Assert.NotNull(reloaded); + Assert.Equal("Hello, how can I help?", reloaded.Text); + } + + [Fact] + public void ToJsonProducesValidJson() + { + string jsonData = """ +{ + "text": "Hello, how can I help?" +} +"""; + + var instance = AnthropicTextBlock.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 = """ +text: Hello, how can I help? + +"""; + + var instance = AnthropicTextBlock.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(yaml); + Assert.NotNull(parsed); + } +} diff --git a/runtime/csharp/Prompty.Core.Tests/Model/wire/AnthropicToolDefinitionConversionTests.cs b/runtime/csharp/Prompty.Core.Tests/Model/wire/AnthropicToolDefinitionConversionTests.cs new file mode 100644 index 00000000..9a1b9960 --- /dev/null +++ b/runtime/csharp/Prompty.Core.Tests/Model/wire/AnthropicToolDefinitionConversionTests.cs @@ -0,0 +1,122 @@ +using Xunit; + +#pragma warning disable IDE0130 +namespace Prompty.Core; +#pragma warning restore IDE0130 + + +public class AnthropicToolDefinitionConversionTests +{ + [Fact] + public void LoadYamlInput() + { + string yamlData = """ +name: get_weather +description: Get the current weather for a city + +"""; + + var instance = AnthropicToolDefinition.FromYaml(yamlData); + + Assert.NotNull(instance); + Assert.Equal("get_weather", instance.Name); + Assert.Equal("Get the current weather for a city", instance.Description); + } + + [Fact] + public void LoadJsonInput() + { + string jsonData = """ +{ + "name": "get_weather", + "description": "Get the current weather for a city" +} +"""; + + var instance = AnthropicToolDefinition.FromJson(jsonData); + Assert.NotNull(instance); + Assert.Equal("get_weather", instance.Name); + Assert.Equal("Get the current weather for a city", instance.Description); + } + + [Fact] + public void RoundtripJson() + { + // Test that FromJson -> ToJson -> FromJson produces equivalent data + string jsonData = """ +{ + "name": "get_weather", + "description": "Get the current weather for a city" +} +"""; + + var original = AnthropicToolDefinition.FromJson(jsonData); + Assert.NotNull(original); + + var json = original.ToJson(); + Assert.False(string.IsNullOrEmpty(json)); + + var reloaded = AnthropicToolDefinition.FromJson(json); + Assert.NotNull(reloaded); + Assert.Equal("get_weather", reloaded.Name); + Assert.Equal("Get the current weather for a city", reloaded.Description); + } + + [Fact] + public void RoundtripYaml() + { + // Test that FromYaml -> ToYaml -> FromYaml produces equivalent data + string yamlData = """ +name: get_weather +description: Get the current weather for a city + +"""; + + var original = AnthropicToolDefinition.FromYaml(yamlData); + Assert.NotNull(original); + + var yaml = original.ToYaml(); + Assert.False(string.IsNullOrEmpty(yaml)); + + var reloaded = AnthropicToolDefinition.FromYaml(yaml); + Assert.NotNull(reloaded); + Assert.Equal("get_weather", reloaded.Name); + Assert.Equal("Get the current weather for a city", reloaded.Description); + } + + [Fact] + public void ToJsonProducesValidJson() + { + string jsonData = """ +{ + "name": "get_weather", + "description": "Get the current weather for a city" +} +"""; + + var instance = AnthropicToolDefinition.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 = """ +name: get_weather +description: Get the current weather for a city + +"""; + + var instance = AnthropicToolDefinition.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(yaml); + Assert.NotNull(parsed); + } +} diff --git a/runtime/csharp/Prompty.Core.Tests/Model/wire/AnthropicToolResultBlockConversionTests.cs b/runtime/csharp/Prompty.Core.Tests/Model/wire/AnthropicToolResultBlockConversionTests.cs new file mode 100644 index 00000000..8de7c2c9 --- /dev/null +++ b/runtime/csharp/Prompty.Core.Tests/Model/wire/AnthropicToolResultBlockConversionTests.cs @@ -0,0 +1,122 @@ +using Xunit; + +#pragma warning disable IDE0130 +namespace Prompty.Core; +#pragma warning restore IDE0130 + + +public class AnthropicToolResultBlockConversionTests +{ + [Fact] + public void LoadYamlInput() + { + string yamlData = """ +tool_use_id: toolu_01A09q90qw90lq917835lq9 +content: 72°F and sunny in Paris + +"""; + + var instance = AnthropicToolResultBlock.FromYaml(yamlData); + + Assert.NotNull(instance); + Assert.Equal("toolu_01A09q90qw90lq917835lq9", instance.ToolUseId); + Assert.Equal("72°F and sunny in Paris", instance.Content); + } + + [Fact] + public void LoadJsonInput() + { + string jsonData = """ +{ + "tool_use_id": "toolu_01A09q90qw90lq917835lq9", + "content": "72°F and sunny in Paris" +} +"""; + + var instance = AnthropicToolResultBlock.FromJson(jsonData); + Assert.NotNull(instance); + Assert.Equal("toolu_01A09q90qw90lq917835lq9", instance.ToolUseId); + Assert.Equal("72°F and sunny in Paris", instance.Content); + } + + [Fact] + public void RoundtripJson() + { + // Test that FromJson -> ToJson -> FromJson produces equivalent data + string jsonData = """ +{ + "tool_use_id": "toolu_01A09q90qw90lq917835lq9", + "content": "72°F and sunny in Paris" +} +"""; + + var original = AnthropicToolResultBlock.FromJson(jsonData); + Assert.NotNull(original); + + var json = original.ToJson(); + Assert.False(string.IsNullOrEmpty(json)); + + var reloaded = AnthropicToolResultBlock.FromJson(json); + Assert.NotNull(reloaded); + Assert.Equal("toolu_01A09q90qw90lq917835lq9", reloaded.ToolUseId); + Assert.Equal("72°F and sunny in Paris", reloaded.Content); + } + + [Fact] + public void RoundtripYaml() + { + // Test that FromYaml -> ToYaml -> FromYaml produces equivalent data + string yamlData = """ +tool_use_id: toolu_01A09q90qw90lq917835lq9 +content: 72°F and sunny in Paris + +"""; + + var original = AnthropicToolResultBlock.FromYaml(yamlData); + Assert.NotNull(original); + + var yaml = original.ToYaml(); + Assert.False(string.IsNullOrEmpty(yaml)); + + var reloaded = AnthropicToolResultBlock.FromYaml(yaml); + Assert.NotNull(reloaded); + Assert.Equal("toolu_01A09q90qw90lq917835lq9", reloaded.ToolUseId); + Assert.Equal("72°F and sunny in Paris", reloaded.Content); + } + + [Fact] + public void ToJsonProducesValidJson() + { + string jsonData = """ +{ + "tool_use_id": "toolu_01A09q90qw90lq917835lq9", + "content": "72°F and sunny in Paris" +} +"""; + + var instance = AnthropicToolResultBlock.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 = """ +tool_use_id: toolu_01A09q90qw90lq917835lq9 +content: 72°F and sunny in Paris + +"""; + + var instance = AnthropicToolResultBlock.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(yaml); + Assert.NotNull(parsed); + } +} diff --git a/runtime/csharp/Prompty.Core.Tests/Model/wire/AnthropicToolUseBlockConversionTests.cs b/runtime/csharp/Prompty.Core.Tests/Model/wire/AnthropicToolUseBlockConversionTests.cs new file mode 100644 index 00000000..114774c5 --- /dev/null +++ b/runtime/csharp/Prompty.Core.Tests/Model/wire/AnthropicToolUseBlockConversionTests.cs @@ -0,0 +1,137 @@ +using Xunit; + +#pragma warning disable IDE0130 +namespace Prompty.Core; +#pragma warning restore IDE0130 + + +public class AnthropicToolUseBlockConversionTests +{ + [Fact] + public void LoadYamlInput() + { + string yamlData = """ +id: toolu_01A09q90qw90lq917835lq9 +name: get_weather +input: + city: Paris + +"""; + + var instance = AnthropicToolUseBlock.FromYaml(yamlData); + + Assert.NotNull(instance); + Assert.Equal("toolu_01A09q90qw90lq917835lq9", instance.Id); + Assert.Equal("get_weather", instance.Name); + } + + [Fact] + public void LoadJsonInput() + { + string jsonData = """ +{ + "id": "toolu_01A09q90qw90lq917835lq9", + "name": "get_weather", + "input": { + "city": "Paris" + } +} +"""; + + var instance = AnthropicToolUseBlock.FromJson(jsonData); + Assert.NotNull(instance); + Assert.Equal("toolu_01A09q90qw90lq917835lq9", instance.Id); + Assert.Equal("get_weather", instance.Name); + } + + [Fact] + public void RoundtripJson() + { + // Test that FromJson -> ToJson -> FromJson produces equivalent data + string jsonData = """ +{ + "id": "toolu_01A09q90qw90lq917835lq9", + "name": "get_weather", + "input": { + "city": "Paris" + } +} +"""; + + var original = AnthropicToolUseBlock.FromJson(jsonData); + Assert.NotNull(original); + + var json = original.ToJson(); + Assert.False(string.IsNullOrEmpty(json)); + + var reloaded = AnthropicToolUseBlock.FromJson(json); + Assert.NotNull(reloaded); + Assert.Equal("toolu_01A09q90qw90lq917835lq9", reloaded.Id); + Assert.Equal("get_weather", reloaded.Name); + } + + [Fact] + public void RoundtripYaml() + { + // Test that FromYaml -> ToYaml -> FromYaml produces equivalent data + string yamlData = """ +id: toolu_01A09q90qw90lq917835lq9 +name: get_weather +input: + city: Paris + +"""; + + var original = AnthropicToolUseBlock.FromYaml(yamlData); + Assert.NotNull(original); + + var yaml = original.ToYaml(); + Assert.False(string.IsNullOrEmpty(yaml)); + + var reloaded = AnthropicToolUseBlock.FromYaml(yaml); + Assert.NotNull(reloaded); + Assert.Equal("toolu_01A09q90qw90lq917835lq9", reloaded.Id); + Assert.Equal("get_weather", reloaded.Name); + } + + [Fact] + public void ToJsonProducesValidJson() + { + string jsonData = """ +{ + "id": "toolu_01A09q90qw90lq917835lq9", + "name": "get_weather", + "input": { + "city": "Paris" + } +} +"""; + + var instance = AnthropicToolUseBlock.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 = """ +id: toolu_01A09q90qw90lq917835lq9 +name: get_weather +input: + city: Paris + +"""; + + var instance = AnthropicToolUseBlock.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(yaml); + Assert.NotNull(parsed); + } +} diff --git a/runtime/csharp/Prompty.Core.Tests/Model/wire/AnthropicUsageConversionTests.cs b/runtime/csharp/Prompty.Core.Tests/Model/wire/AnthropicUsageConversionTests.cs new file mode 100644 index 00000000..4c54a2ed --- /dev/null +++ b/runtime/csharp/Prompty.Core.Tests/Model/wire/AnthropicUsageConversionTests.cs @@ -0,0 +1,122 @@ +using Xunit; + +#pragma warning disable IDE0130 +namespace Prompty.Core; +#pragma warning restore IDE0130 + + +public class AnthropicUsageConversionTests +{ + [Fact] + public void LoadYamlInput() + { + string yamlData = """ +input_tokens: 150 +output_tokens: 42 + +"""; + + var instance = AnthropicUsage.FromYaml(yamlData); + + Assert.NotNull(instance); + Assert.Equal(150, instance.InputTokens); + Assert.Equal(42, instance.OutputTokens); + } + + [Fact] + public void LoadJsonInput() + { + string jsonData = """ +{ + "input_tokens": 150, + "output_tokens": 42 +} +"""; + + var instance = AnthropicUsage.FromJson(jsonData); + Assert.NotNull(instance); + Assert.Equal(150, instance.InputTokens); + Assert.Equal(42, instance.OutputTokens); + } + + [Fact] + public void RoundtripJson() + { + // Test that FromJson -> ToJson -> FromJson produces equivalent data + string jsonData = """ +{ + "input_tokens": 150, + "output_tokens": 42 +} +"""; + + var original = AnthropicUsage.FromJson(jsonData); + Assert.NotNull(original); + + var json = original.ToJson(); + Assert.False(string.IsNullOrEmpty(json)); + + var reloaded = AnthropicUsage.FromJson(json); + Assert.NotNull(reloaded); + Assert.Equal(150, reloaded.InputTokens); + Assert.Equal(42, reloaded.OutputTokens); + } + + [Fact] + public void RoundtripYaml() + { + // Test that FromYaml -> ToYaml -> FromYaml produces equivalent data + string yamlData = """ +input_tokens: 150 +output_tokens: 42 + +"""; + + var original = AnthropicUsage.FromYaml(yamlData); + Assert.NotNull(original); + + var yaml = original.ToYaml(); + Assert.False(string.IsNullOrEmpty(yaml)); + + var reloaded = AnthropicUsage.FromYaml(yaml); + Assert.NotNull(reloaded); + Assert.Equal(150, reloaded.InputTokens); + Assert.Equal(42, reloaded.OutputTokens); + } + + [Fact] + public void ToJsonProducesValidJson() + { + string jsonData = """ +{ + "input_tokens": 150, + "output_tokens": 42 +} +"""; + + var instance = AnthropicUsage.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 = """ +input_tokens: 150 +output_tokens: 42 + +"""; + + var instance = AnthropicUsage.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(yaml); + Assert.NotNull(parsed); + } +} diff --git a/runtime/csharp/Prompty.Core.Tests/Model/wire/AnthropicWireMessageConversionTests.cs b/runtime/csharp/Prompty.Core.Tests/Model/wire/AnthropicWireMessageConversionTests.cs new file mode 100644 index 00000000..57806912 --- /dev/null +++ b/runtime/csharp/Prompty.Core.Tests/Model/wire/AnthropicWireMessageConversionTests.cs @@ -0,0 +1,112 @@ +using Xunit; + +#pragma warning disable IDE0130 +namespace Prompty.Core; +#pragma warning restore IDE0130 + + +public class AnthropicWireMessageConversionTests +{ + [Fact] + public void LoadYamlInput() + { + string yamlData = """ +role: user + +"""; + + var instance = AnthropicWireMessage.FromYaml(yamlData); + + Assert.NotNull(instance); + Assert.Equal("user", instance.Role); + } + + [Fact] + public void LoadJsonInput() + { + string jsonData = """ +{ + "role": "user" +} +"""; + + var instance = AnthropicWireMessage.FromJson(jsonData); + Assert.NotNull(instance); + Assert.Equal("user", instance.Role); + } + + [Fact] + public void RoundtripJson() + { + // Test that FromJson -> ToJson -> FromJson produces equivalent data + string jsonData = """ +{ + "role": "user" +} +"""; + + var original = AnthropicWireMessage.FromJson(jsonData); + Assert.NotNull(original); + + var json = original.ToJson(); + Assert.False(string.IsNullOrEmpty(json)); + + var reloaded = AnthropicWireMessage.FromJson(json); + Assert.NotNull(reloaded); + Assert.Equal("user", reloaded.Role); + } + + [Fact] + public void RoundtripYaml() + { + // Test that FromYaml -> ToYaml -> FromYaml produces equivalent data + string yamlData = """ +role: user + +"""; + + var original = AnthropicWireMessage.FromYaml(yamlData); + Assert.NotNull(original); + + var yaml = original.ToYaml(); + Assert.False(string.IsNullOrEmpty(yaml)); + + var reloaded = AnthropicWireMessage.FromYaml(yaml); + Assert.NotNull(reloaded); + Assert.Equal("user", reloaded.Role); + } + + [Fact] + public void ToJsonProducesValidJson() + { + string jsonData = """ +{ + "role": "user" +} +"""; + + var instance = AnthropicWireMessage.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 = """ +role: user + +"""; + + var instance = AnthropicWireMessage.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(yaml); + Assert.NotNull(parsed); + } +} diff --git a/runtime/csharp/Prompty.Core.Tests/PipelineTests.cs b/runtime/csharp/Prompty.Core.Tests/PipelineTests.cs index f4412f0f..2ef49f99 100644 --- a/runtime/csharp/Prompty.Core.Tests/PipelineTests.cs +++ b/runtime/csharp/Prompty.Core.Tests/PipelineTests.cs @@ -166,30 +166,30 @@ public void RegisterAndGet_Processor() [Fact] public void GetRenderer_ThrowsInvokerError_WhenMissing() { - var ex = Assert.Throws(() => InvokerRegistry.GetRenderer("nope")); + var ex = Assert.Throws(() => InvokerRegistry.GetRenderer("nope")); Assert.Equal("renderer", ex.Group); - Assert.Equal("nope", ex.Key); + Assert.Equal("nope", ex.InvokerKey); Assert.Contains("Register", ex.Message); } [Fact] public void GetParser_ThrowsInvokerError_WhenMissing() { - var ex = Assert.Throws(() => InvokerRegistry.GetParser("nope")); + var ex = Assert.Throws(() => InvokerRegistry.GetParser("nope")); Assert.Equal("parser", ex.Group); } [Fact] public void GetExecutor_ThrowsInvokerError_WhenMissing() { - var ex = Assert.Throws(() => InvokerRegistry.GetExecutor("nope")); + var ex = Assert.Throws(() => InvokerRegistry.GetExecutor("nope")); Assert.Equal("executor", ex.Group); } [Fact] public void GetProcessor_ThrowsInvokerError_WhenMissing() { - var ex = Assert.Throws(() => InvokerRegistry.GetProcessor("nope")); + var ex = Assert.Throws(() => InvokerRegistry.GetProcessor("nope")); Assert.Equal("processor", ex.Group); } diff --git a/runtime/csharp/Prompty.Core/InvokerRegistry.cs b/runtime/csharp/Prompty.Core/InvokerRegistry.cs index 19ca0936..9ac632f6 100644 --- a/runtime/csharp/Prompty.Core/InvokerRegistry.cs +++ b/runtime/csharp/Prompty.Core/InvokerRegistry.cs @@ -7,16 +7,16 @@ namespace Prompty.Core; /// /// Thrown when a required invoker (renderer, parser, executor, or processor) is not registered. /// -public class InvokerError : InvalidOperationException +public class InvokerNotFoundException : InvalidOperationException { public string Group { get; } - public string Key { get; } + public string InvokerKey { get; } - public InvokerError(string group, string key) + public InvokerNotFoundException(string group, string key) : base($"No {group} registered for '{key}'. Register one via InvokerRegistry.Register{GetFriendlyGroup(group)}().") { Group = group; - Key = key; + InvokerKey = key; } private static string GetFriendlyGroup(string group) => group switch @@ -57,16 +57,16 @@ public static void RegisterProcessor(string key, IProcessor processor) // --- Lookup --- public static IRenderer GetRenderer(string key) - => _renderers.TryGetValue(key, out var r) ? r : throw new InvokerError("renderer", key); + => _renderers.TryGetValue(key, out var r) ? r : throw new InvokerNotFoundException("renderer", key); public static IParser GetParser(string key) - => _parsers.TryGetValue(key, out var p) ? p : throw new InvokerError("parser", key); + => _parsers.TryGetValue(key, out var p) ? p : throw new InvokerNotFoundException("parser", key); public static IExecutor GetExecutor(string key) - => _executors.TryGetValue(key, out var e) ? e : throw new InvokerError("executor", key); + => _executors.TryGetValue(key, out var e) ? e : throw new InvokerNotFoundException("executor", key); public static IProcessor GetProcessor(string key) - => _processors.TryGetValue(key, out var p) ? p : throw new InvokerError("processor", key); + => _processors.TryGetValue(key, out var p) ? p : throw new InvokerNotFoundException("processor", key); // --- Query --- diff --git a/runtime/csharp/Prompty.Core/Model/core/FileNotFoundError.cs b/runtime/csharp/Prompty.Core/Model/core/FileNotFoundError.cs new file mode 100644 index 00000000..42e4f636 --- /dev/null +++ b/runtime/csharp/Prompty.Core/Model/core/FileNotFoundError.cs @@ -0,0 +1,170 @@ +// Copyright (c) Microsoft. All rights reserved. +using System.Text.Json; +using YamlDotNet.Serialization; + +#pragma warning disable IDE0130 +namespace Prompty.Core; +#pragma warning restore IDE0130 + +/// +/// Raised when a referenced file cannot be found. This applies to both +/// +/// .prompty files and ${file:path} references in frontmatter. +/// +public partial class FileNotFoundError +{ + /// + /// The shorthand property name for this type, if any. + /// + public static string? ShorthandProperty => null; + + /// + /// Initializes a new instance of . + /// +#pragma warning disable CS8618 + public FileNotFoundError() + { + } +#pragma warning restore CS8618 + + /// + /// Human-readable error message + /// + public string Message { get; set; } = string.Empty; + + /// + /// The file path that could not be resolved + /// + public string Path { get; set; } = string.Empty; + + + + #region Load Methods + + /// + /// Load a FileNotFoundError instance from a dictionary. + /// + /// The dictionary containing the data. + /// Optional context with pre/post processing callbacks. + /// The loaded FileNotFoundError instance. + public static FileNotFoundError Load(Dictionary data, LoadContext? context = null) + { + if (context is not null) + { + data = context.ProcessInput(data); + } + + + // Create new instance + var instance = new FileNotFoundError(); + + + if (data.TryGetValue("message", out var messageValue) && messageValue is not null) + { + instance.Message = messageValue?.ToString()!; + } + + if (data.TryGetValue("path", out var pathValue) && pathValue is not null) + { + instance.Path = pathValue?.ToString()!; + } + + if (context is not null) + { + instance = context.ProcessOutput(instance); + } + return instance; + } + + + #endregion + + #region Save Methods + + /// + /// Save the FileNotFoundError instance to a dictionary. + /// + /// Optional context with pre/post processing callbacks. + /// The dictionary representation of this instance. + public Dictionary Save(SaveContext? context = null) + { + var obj = this; + if (context is not null) + { + obj = context.ProcessObject(obj); + } + + + var result = new Dictionary(); + + + result["message"] = obj.Message; + + + result["path"] = obj.Path; + + + if (context is not null) + { + result = context.ProcessDict(result); + } + + return result; + } + + + /// + /// Convert the FileNotFoundError instance to a YAML string. + /// + /// Optional context with pre/post processing callbacks. + /// The YAML string representation of this instance. + public string ToYaml(SaveContext? context = null) + { + context ??= new SaveContext(); + return context.ToYaml(Save(context)); + } + + /// + /// Convert the FileNotFoundError instance to a JSON string. + /// + /// Optional context with pre/post processing callbacks. + /// Whether to indent the output. Defaults to true. + /// The JSON string representation of this instance. + public string ToJson(SaveContext? context = null, bool indent = true) + { + context ??= new SaveContext(); + return context.ToJson(Save(context), indent); + } + + /// + /// Load a FileNotFoundError instance from a JSON string. + /// + /// The JSON string to parse. + /// Optional context with pre/post processing callbacks. + /// The loaded FileNotFoundError instance. + public static FileNotFoundError FromJson(string json, LoadContext? context = null) + { + using var doc = JsonDocument.Parse(json); + Dictionary dict; + dict = JsonSerializer.Deserialize>(json, JsonUtils.Options) + ?? throw new ArgumentException("Failed to parse JSON as dictionary"); + + return Load(dict, context); + } + + /// + /// Load a FileNotFoundError instance from a YAML string. + /// + /// The YAML string to parse. + /// Optional context with pre/post processing callbacks. + /// The loaded FileNotFoundError instance. + public static FileNotFoundError FromYaml(string yaml, LoadContext? context = null) + { + var dict = YamlUtils.Deserializer.Deserialize>(yaml) + ?? throw new ArgumentException("Failed to parse YAML as dictionary"); + + return Load(dict, context); + } + + #endregion +} diff --git a/runtime/csharp/Prompty.Core/Model/core/InvokerError.cs b/runtime/csharp/Prompty.Core/Model/core/InvokerError.cs new file mode 100644 index 00000000..840c4b65 --- /dev/null +++ b/runtime/csharp/Prompty.Core/Model/core/InvokerError.cs @@ -0,0 +1,185 @@ +// Copyright (c) Microsoft. All rights reserved. +using System.Text.Json; +using YamlDotNet.Serialization; + +#pragma warning disable IDE0130 +namespace Prompty.Core; +#pragma warning restore IDE0130 + +/// +/// Raised when no invoker implementation is registered for a given component +/// +/// and key. For example, if no renderer is registered for the key "jinja2", +/// +/// an InvokerError is raised. +/// +public partial class InvokerError +{ + /// + /// The shorthand property name for this type, if any. + /// + public static string? ShorthandProperty => null; + + /// + /// Initializes a new instance of . + /// +#pragma warning disable CS8618 + public InvokerError() + { + } +#pragma warning restore CS8618 + + /// + /// Human-readable error message + /// + public string Message { get; set; } = string.Empty; + + /// + /// The pipeline component type that was being looked up (e.g., 'renderer', 'parser', 'executor', 'processor') + /// + public string Component { get; set; } = string.Empty; + + /// + /// The registration key that was not found + /// + public string Key { get; set; } = string.Empty; + + + + #region Load Methods + + /// + /// Load a InvokerError instance from a dictionary. + /// + /// The dictionary containing the data. + /// Optional context with pre/post processing callbacks. + /// The loaded InvokerError instance. + public static InvokerError Load(Dictionary data, LoadContext? context = null) + { + if (context is not null) + { + data = context.ProcessInput(data); + } + + + // Create new instance + var instance = new InvokerError(); + + + if (data.TryGetValue("message", out var messageValue) && messageValue is not null) + { + instance.Message = messageValue?.ToString()!; + } + + if (data.TryGetValue("component", out var componentValue) && componentValue is not null) + { + instance.Component = componentValue?.ToString()!; + } + + if (data.TryGetValue("key", out var keyValue) && keyValue is not null) + { + instance.Key = keyValue?.ToString()!; + } + + if (context is not null) + { + instance = context.ProcessOutput(instance); + } + return instance; + } + + + #endregion + + #region Save Methods + + /// + /// Save the InvokerError instance to a dictionary. + /// + /// Optional context with pre/post processing callbacks. + /// The dictionary representation of this instance. + public Dictionary Save(SaveContext? context = null) + { + var obj = this; + if (context is not null) + { + obj = context.ProcessObject(obj); + } + + + var result = new Dictionary(); + + + result["message"] = obj.Message; + + + result["component"] = obj.Component; + + + result["key"] = obj.Key; + + + if (context is not null) + { + result = context.ProcessDict(result); + } + + return result; + } + + + /// + /// Convert the InvokerError instance to a YAML string. + /// + /// Optional context with pre/post processing callbacks. + /// The YAML string representation of this instance. + public string ToYaml(SaveContext? context = null) + { + context ??= new SaveContext(); + return context.ToYaml(Save(context)); + } + + /// + /// Convert the InvokerError instance to a JSON string. + /// + /// Optional context with pre/post processing callbacks. + /// Whether to indent the output. Defaults to true. + /// The JSON string representation of this instance. + public string ToJson(SaveContext? context = null, bool indent = true) + { + context ??= new SaveContext(); + return context.ToJson(Save(context), indent); + } + + /// + /// Load a InvokerError instance from a JSON string. + /// + /// The JSON string to parse. + /// Optional context with pre/post processing callbacks. + /// The loaded InvokerError instance. + public static InvokerError FromJson(string json, LoadContext? context = null) + { + using var doc = JsonDocument.Parse(json); + Dictionary dict; + dict = JsonSerializer.Deserialize>(json, JsonUtils.Options) + ?? throw new ArgumentException("Failed to parse JSON as dictionary"); + + return Load(dict, context); + } + + /// + /// Load a InvokerError instance from a YAML string. + /// + /// The YAML string to parse. + /// Optional context with pre/post processing callbacks. + /// The loaded InvokerError instance. + public static InvokerError FromYaml(string yaml, LoadContext? context = null) + { + var dict = YamlUtils.Deserializer.Deserialize>(yaml) + ?? throw new ArgumentException("Failed to parse YAML as dictionary"); + + return Load(dict, context); + } + + #endregion +} diff --git a/runtime/csharp/Prompty.Core/Model/core/ValidationError.cs b/runtime/csharp/Prompty.Core/Model/core/ValidationError.cs new file mode 100644 index 00000000..b2a9192a --- /dev/null +++ b/runtime/csharp/Prompty.Core/Model/core/ValidationError.cs @@ -0,0 +1,183 @@ +// Copyright (c) Microsoft. All rights reserved. +using System.Text.Json; +using YamlDotNet.Serialization; + +#pragma warning disable IDE0130 +namespace Prompty.Core; +#pragma warning restore IDE0130 + +/// +/// Raised when input validation fails. Each ValidationError describes a +/// +/// single property that did not satisfy its constraint. +/// +public partial class ValidationError +{ + /// + /// The shorthand property name for this type, if any. + /// + public static string? ShorthandProperty => null; + + /// + /// Initializes a new instance of . + /// +#pragma warning disable CS8618 + public ValidationError() + { + } +#pragma warning restore CS8618 + + /// + /// Human-readable error message + /// + public string Message { get; set; } = string.Empty; + + /// + /// The name of the property that failed validation + /// + public string Property { get; set; } = string.Empty; + + /// + /// The constraint that was violated (e.g., 'required', 'type') + /// + public string Constraint { get; set; } = string.Empty; + + + + #region Load Methods + + /// + /// Load a ValidationError instance from a dictionary. + /// + /// The dictionary containing the data. + /// Optional context with pre/post processing callbacks. + /// The loaded ValidationError instance. + public static ValidationError Load(Dictionary data, LoadContext? context = null) + { + if (context is not null) + { + data = context.ProcessInput(data); + } + + + // Create new instance + var instance = new ValidationError(); + + + if (data.TryGetValue("message", out var messageValue) && messageValue is not null) + { + instance.Message = messageValue?.ToString()!; + } + + if (data.TryGetValue("property", out var propertyValue) && propertyValue is not null) + { + instance.Property = propertyValue?.ToString()!; + } + + if (data.TryGetValue("constraint", out var constraintValue) && constraintValue is not null) + { + instance.Constraint = constraintValue?.ToString()!; + } + + if (context is not null) + { + instance = context.ProcessOutput(instance); + } + return instance; + } + + + #endregion + + #region Save Methods + + /// + /// Save the ValidationError instance to a dictionary. + /// + /// Optional context with pre/post processing callbacks. + /// The dictionary representation of this instance. + public Dictionary Save(SaveContext? context = null) + { + var obj = this; + if (context is not null) + { + obj = context.ProcessObject(obj); + } + + + var result = new Dictionary(); + + + result["message"] = obj.Message; + + + result["property"] = obj.Property; + + + result["constraint"] = obj.Constraint; + + + if (context is not null) + { + result = context.ProcessDict(result); + } + + return result; + } + + + /// + /// Convert the ValidationError instance to a YAML string. + /// + /// Optional context with pre/post processing callbacks. + /// The YAML string representation of this instance. + public string ToYaml(SaveContext? context = null) + { + context ??= new SaveContext(); + return context.ToYaml(Save(context)); + } + + /// + /// Convert the ValidationError instance to a JSON string. + /// + /// Optional context with pre/post processing callbacks. + /// Whether to indent the output. Defaults to true. + /// The JSON string representation of this instance. + public string ToJson(SaveContext? context = null, bool indent = true) + { + context ??= new SaveContext(); + return context.ToJson(Save(context), indent); + } + + /// + /// Load a ValidationError instance from a JSON string. + /// + /// The JSON string to parse. + /// Optional context with pre/post processing callbacks. + /// The loaded ValidationError instance. + public static ValidationError FromJson(string json, LoadContext? context = null) + { + using var doc = JsonDocument.Parse(json); + Dictionary dict; + dict = JsonSerializer.Deserialize>(json, JsonUtils.Options) + ?? throw new ArgumentException("Failed to parse JSON as dictionary"); + + return Load(dict, context); + } + + /// + /// Load a ValidationError instance from a YAML string. + /// + /// The YAML string to parse. + /// Optional context with pre/post processing callbacks. + /// The loaded ValidationError instance. + public static ValidationError FromYaml(string yaml, LoadContext? context = null) + { + var dict = YamlUtils.Deserializer.Deserialize>(yaml) + ?? throw new ArgumentException("Failed to parse YAML as dictionary"); + + return Load(dict, context); + } + + #endregion +} diff --git a/runtime/csharp/Prompty.Core/Model/core/ValidationResult.cs b/runtime/csharp/Prompty.Core/Model/core/ValidationResult.cs new file mode 100644 index 00000000..65d347ba --- /dev/null +++ b/runtime/csharp/Prompty.Core/Model/core/ValidationResult.cs @@ -0,0 +1,239 @@ +// Copyright (c) Microsoft. All rights reserved. +using System.Text.Json; +using YamlDotNet.Serialization; + +#pragma warning disable IDE0130 +namespace Prompty.Core; +#pragma warning restore IDE0130 + +/// +/// The result of validating inputs against an agent's inputSchema. +/// +/// Returned by `validate_inputs` (§12.2) to indicate whether all +/// +/// required inputs are present and satisfy their constraints. +/// +public partial class ValidationResult +{ + /// + /// The shorthand property name for this type, if any. + /// + public static string? ShorthandProperty => null; + + /// + /// Initializes a new instance of . + /// +#pragma warning disable CS8618 + public ValidationResult() + { + } +#pragma warning restore CS8618 + + /// + /// Whether all inputs passed validation + /// + public bool Valid { get; set; } = false; + + /// + /// List of validation errors (empty when valid is true) + /// + public IList Errors { get; set; } = []; + + + + #region Load Methods + + /// + /// Load a ValidationResult instance from a dictionary. + /// + /// The dictionary containing the data. + /// Optional context with pre/post processing callbacks. + /// The loaded ValidationResult instance. + public static ValidationResult Load(Dictionary data, LoadContext? context = null) + { + if (context is not null) + { + data = context.ProcessInput(data); + } + + + // Create new instance + var instance = new ValidationResult(); + + + if (data.TryGetValue("valid", out var validValue) && validValue is not null) + { + instance.Valid = Convert.ToBoolean(validValue); + } + + if (data.TryGetValue("errors", out var errorsValue) && errorsValue is not null) + { + instance.Errors = LoadErrors(errorsValue, context); + } + + if (context is not null) + { + instance = context.ProcessOutput(instance); + } + return instance; + } + + + /// + /// Load a list of ValidationError from a dictionary or list. + /// + public static IList LoadErrors(object data, LoadContext? context) + { + var result = new List(); + + if (data is Dictionary dict) + { + // Convert named dictionary to list + foreach (var kvp in dict) + { + if (kvp.Value is IEnumerable) + { + throw new ArgumentException( + $"Invalid 'errors' format: key '{kvp.Key}' has an array value. " + + $"'errors' must be a flat list of objects or a name-keyed dict — " + + "not a nested {" + kvp.Key + ": [...]} structure."); + } + var itemDict = kvp.Value.GetDictionary(); + if (itemDict.Count > 0) + { + // Value is an object, add name to it + itemDict["name"] = kvp.Key; + result.Add(ValidationError.Load(itemDict, context)); + } + else + { + // Value is a scalar, use it as the primary property + var newDict = new Dictionary + { + ["name"] = kvp.Key, + ["message"] = kvp.Value + }; + result.Add(ValidationError.Load(newDict, context)); + } + } + } + else if (data is IEnumerable list) + { + foreach (var item in list) + { + var itemDict = item.GetDictionary(ValidationError.ShorthandProperty); + if (itemDict.Count > 0) + { + result.Add(ValidationError.Load(itemDict, context)); + } + } + } + + return result; + } + + + #endregion + + #region Save Methods + + /// + /// Save the ValidationResult instance to a dictionary. + /// + /// Optional context with pre/post processing callbacks. + /// The dictionary representation of this instance. + public Dictionary Save(SaveContext? context = null) + { + var obj = this; + if (context is not null) + { + obj = context.ProcessObject(obj); + } + + + var result = new Dictionary(); + + + result["valid"] = obj.Valid; + + + result["errors"] = SaveErrors(obj.Errors, context); + + + if (context is not null) + { + result = context.ProcessDict(result); + } + + return result; + } + + + /// + /// Save a list of ValidationError to object or array format. + /// + public static object SaveErrors(IList items, SaveContext? context) + { + context ??= new SaveContext(); + + // This collection type does not have a 'name' property, only array format is supported + return items.Select(item => item.Save(context)).ToList(); + + } + + + /// + /// Convert the ValidationResult instance to a YAML string. + /// + /// Optional context with pre/post processing callbacks. + /// The YAML string representation of this instance. + public string ToYaml(SaveContext? context = null) + { + context ??= new SaveContext(); + return context.ToYaml(Save(context)); + } + + /// + /// Convert the ValidationResult instance to a JSON string. + /// + /// Optional context with pre/post processing callbacks. + /// Whether to indent the output. Defaults to true. + /// The JSON string representation of this instance. + public string ToJson(SaveContext? context = null, bool indent = true) + { + context ??= new SaveContext(); + return context.ToJson(Save(context), indent); + } + + /// + /// Load a ValidationResult instance from a JSON string. + /// + /// The JSON string to parse. + /// Optional context with pre/post processing callbacks. + /// The loaded ValidationResult instance. + public static ValidationResult FromJson(string json, LoadContext? context = null) + { + using var doc = JsonDocument.Parse(json); + Dictionary dict; + dict = JsonSerializer.Deserialize>(json, JsonUtils.Options) + ?? throw new ArgumentException("Failed to parse JSON as dictionary"); + + return Load(dict, context); + } + + /// + /// Load a ValidationResult instance from a YAML string. + /// + /// The YAML string to parse. + /// Optional context with pre/post processing callbacks. + /// The loaded ValidationResult instance. + public static ValidationResult FromYaml(string yaml, LoadContext? context = null) + { + var dict = YamlUtils.Deserializer.Deserialize>(yaml) + ?? throw new ArgumentException("Failed to parse YAML as dictionary"); + + return Load(dict, context); + } + + #endregion +} diff --git a/runtime/csharp/Prompty.Core/Model/streaming/StreamOptions.cs b/runtime/csharp/Prompty.Core/Model/streaming/StreamOptions.cs new file mode 100644 index 00000000..02fb105d --- /dev/null +++ b/runtime/csharp/Prompty.Core/Model/streaming/StreamOptions.cs @@ -0,0 +1,160 @@ +// Copyright (c) Microsoft. All rights reserved. +using System.Text.Json; +using YamlDotNet.Serialization; + +#pragma warning disable IDE0130 +namespace Prompty.Core; +#pragma warning restore IDE0130 + +/// +/// Options controlling streaming behavior for LLM API calls. +/// +/// Passed alongside the model options when streaming is enabled. +/// +public partial class StreamOptions +{ + /// + /// The shorthand property name for this type, if any. + /// + public static string? ShorthandProperty => null; + + /// + /// Initializes a new instance of . + /// +#pragma warning disable CS8618 + public StreamOptions() + { + } +#pragma warning restore CS8618 + + /// + /// When true, the final streaming chunk includes token usage statistics + /// + public bool? IncludeUsage { get; set; } + + + + #region Load Methods + + /// + /// Load a StreamOptions instance from a dictionary. + /// + /// The dictionary containing the data. + /// Optional context with pre/post processing callbacks. + /// The loaded StreamOptions instance. + public static StreamOptions Load(Dictionary data, LoadContext? context = null) + { + if (context is not null) + { + data = context.ProcessInput(data); + } + + + // Create new instance + var instance = new StreamOptions(); + + + if (data.TryGetValue("includeUsage", out var includeUsageValue) && includeUsageValue is not null) + { + instance.IncludeUsage = Convert.ToBoolean(includeUsageValue); + } + + if (context is not null) + { + instance = context.ProcessOutput(instance); + } + return instance; + } + + + #endregion + + #region Save Methods + + /// + /// Save the StreamOptions instance to a dictionary. + /// + /// Optional context with pre/post processing callbacks. + /// The dictionary representation of this instance. + public Dictionary Save(SaveContext? context = null) + { + var obj = this; + if (context is not null) + { + obj = context.ProcessObject(obj); + } + + + var result = new Dictionary(); + + + if (obj.IncludeUsage is not null) + { + result["includeUsage"] = obj.IncludeUsage; + } + + + if (context is not null) + { + result = context.ProcessDict(result); + } + + return result; + } + + + /// + /// Convert the StreamOptions instance to a YAML string. + /// + /// Optional context with pre/post processing callbacks. + /// The YAML string representation of this instance. + public string ToYaml(SaveContext? context = null) + { + context ??= new SaveContext(); + return context.ToYaml(Save(context)); + } + + /// + /// Convert the StreamOptions instance to a JSON string. + /// + /// Optional context with pre/post processing callbacks. + /// Whether to indent the output. Defaults to true. + /// The JSON string representation of this instance. + public string ToJson(SaveContext? context = null, bool indent = true) + { + context ??= new SaveContext(); + return context.ToJson(Save(context), indent); + } + + /// + /// Load a StreamOptions instance from a JSON string. + /// + /// The JSON string to parse. + /// Optional context with pre/post processing callbacks. + /// The loaded StreamOptions instance. + public static StreamOptions FromJson(string json, LoadContext? context = null) + { + using var doc = JsonDocument.Parse(json); + Dictionary dict; + dict = JsonSerializer.Deserialize>(json, JsonUtils.Options) + ?? throw new ArgumentException("Failed to parse JSON as dictionary"); + + return Load(dict, context); + } + + /// + /// Load a StreamOptions instance from a YAML string. + /// + /// The YAML string to parse. + /// Optional context with pre/post processing callbacks. + /// The loaded StreamOptions instance. + public static StreamOptions FromYaml(string yaml, LoadContext? context = null) + { + var dict = YamlUtils.Deserializer.Deserialize>(yaml) + ?? throw new ArgumentException("Failed to parse YAML as dictionary"); + + return Load(dict, context); + } + + #endregion +} diff --git a/runtime/csharp/Prompty.Core/Model/tracing/TraceFile.cs b/runtime/csharp/Prompty.Core/Model/tracing/TraceFile.cs new file mode 100644 index 00000000..e931d2c5 --- /dev/null +++ b/runtime/csharp/Prompty.Core/Model/tracing/TraceFile.cs @@ -0,0 +1,181 @@ +// Copyright (c) Microsoft. All rights reserved. +using System.Text.Json; +using YamlDotNet.Serialization; + +#pragma warning disable IDE0130 +namespace Prompty.Core; +#pragma warning restore IDE0130 + +/// +/// The top-level .tracy file structure written by the file backend (§3.6.1). +/// +public partial class TraceFile +{ + /// + /// The shorthand property name for this type, if any. + /// + public static string? ShorthandProperty => null; + + /// + /// Initializes a new instance of . + /// +#pragma warning disable CS8618 + public TraceFile() + { + } +#pragma warning restore CS8618 + + /// + /// Language/runtime name (e.g., 'python', 'csharp', 'javascript') + /// + public string Runtime { get; set; } = string.Empty; + + /// + /// Prompty library version + /// + public string Version { get; set; } = string.Empty; + + /// + /// The root trace span + /// + public TraceSpan Trace { get; set; } + + + + #region Load Methods + + /// + /// Load a TraceFile instance from a dictionary. + /// + /// The dictionary containing the data. + /// Optional context with pre/post processing callbacks. + /// The loaded TraceFile instance. + public static TraceFile Load(Dictionary data, LoadContext? context = null) + { + if (context is not null) + { + data = context.ProcessInput(data); + } + + + // Create new instance + var instance = new TraceFile(); + + + if (data.TryGetValue("runtime", out var runtimeValue) && runtimeValue is not null) + { + instance.Runtime = runtimeValue?.ToString()!; + } + + if (data.TryGetValue("version", out var versionValue) && versionValue is not null) + { + instance.Version = versionValue?.ToString()!; + } + + if (data.TryGetValue("trace", out var traceValue) && traceValue is not null) + { + instance.Trace = TraceSpan.Load(traceValue.GetDictionary(TraceSpan.ShorthandProperty), context); + } + + if (context is not null) + { + instance = context.ProcessOutput(instance); + } + return instance; + } + + + #endregion + + #region Save Methods + + /// + /// Save the TraceFile instance to a dictionary. + /// + /// Optional context with pre/post processing callbacks. + /// The dictionary representation of this instance. + public Dictionary Save(SaveContext? context = null) + { + var obj = this; + if (context is not null) + { + obj = context.ProcessObject(obj); + } + + + var result = new Dictionary(); + + + result["runtime"] = obj.Runtime; + + + result["version"] = obj.Version; + + + result["trace"] = obj.Trace?.Save(context); + + + if (context is not null) + { + result = context.ProcessDict(result); + } + + return result; + } + + + /// + /// Convert the TraceFile instance to a YAML string. + /// + /// Optional context with pre/post processing callbacks. + /// The YAML string representation of this instance. + public string ToYaml(SaveContext? context = null) + { + context ??= new SaveContext(); + return context.ToYaml(Save(context)); + } + + /// + /// Convert the TraceFile instance to a JSON string. + /// + /// Optional context with pre/post processing callbacks. + /// Whether to indent the output. Defaults to true. + /// The JSON string representation of this instance. + public string ToJson(SaveContext? context = null, bool indent = true) + { + context ??= new SaveContext(); + return context.ToJson(Save(context), indent); + } + + /// + /// Load a TraceFile instance from a JSON string. + /// + /// The JSON string to parse. + /// Optional context with pre/post processing callbacks. + /// The loaded TraceFile instance. + public static TraceFile FromJson(string json, LoadContext? context = null) + { + using var doc = JsonDocument.Parse(json); + Dictionary dict; + dict = JsonSerializer.Deserialize>(json, JsonUtils.Options) + ?? throw new ArgumentException("Failed to parse JSON as dictionary"); + + return Load(dict, context); + } + + /// + /// Load a TraceFile instance from a YAML string. + /// + /// The YAML string to parse. + /// Optional context with pre/post processing callbacks. + /// The loaded TraceFile instance. + public static TraceFile FromYaml(string yaml, LoadContext? context = null) + { + var dict = YamlUtils.Deserializer.Deserialize>(yaml) + ?? throw new ArgumentException("Failed to parse YAML as dictionary"); + + return Load(dict, context); + } + + #endregion +} diff --git a/runtime/csharp/Prompty.Core/Model/tracing/TraceSpan.cs b/runtime/csharp/Prompty.Core/Model/tracing/TraceSpan.cs new file mode 100644 index 00000000..5c7f746f --- /dev/null +++ b/runtime/csharp/Prompty.Core/Model/tracing/TraceSpan.cs @@ -0,0 +1,284 @@ +// Copyright (c) Microsoft. All rights reserved. +using System.Text.Json; +using YamlDotNet.Serialization; + +#pragma warning disable IDE0130 +namespace Prompty.Core; +#pragma warning restore IDE0130 + +/// +/// A single trace span capturing one pipeline stage or function invocation. +/// +/// Spans nest via the `__frames` field to form a tree representing the +/// +/// full execution (§3.6.1). +/// +public partial class TraceSpan +{ + /// + /// The shorthand property name for this type, if any. + /// + public static string? ShorthandProperty => null; + + /// + /// Initializes a new instance of . + /// +#pragma warning disable CS8618 + public TraceSpan() + { + } +#pragma warning restore CS8618 + + /// + /// The name of this span (typically the function signature) + /// + public string Name { get; set; } = string.Empty; + + /// + /// Timing information for this span + /// + public TraceTime _Time { get; set; } + + /// + /// Fully-qualified function signature that produced this span + /// + public string? Signature { get; set; } + + /// + /// Serialized input parameters (redacted per §3.4) + /// + public IDictionary? Inputs { get; set; } + + /// + /// Serialized return value or error information (redacted per §3.4) + /// + public object? Output { get; set; } + + /// + /// Error message if the span ended with an exception + /// + public string? Error { get; set; } + + /// + /// Aggregated token usage hoisted from child spans (§3.5) + /// + public TokenUsage? _Usage { get; set; } + + /// + /// Additional span attributes (e.g., OpenTelemetry GenAI attributes) + /// + public IDictionary? Attributes { get; set; } + + /// + /// Nested child spans forming the execution tree (recursive; each element is a TraceSpan) + /// + public IList? _Frames { get; set; } + + + + #region Load Methods + + /// + /// Load a TraceSpan instance from a dictionary. + /// + /// The dictionary containing the data. + /// Optional context with pre/post processing callbacks. + /// The loaded TraceSpan instance. + public static TraceSpan Load(Dictionary data, LoadContext? context = null) + { + if (context is not null) + { + data = context.ProcessInput(data); + } + + + // Create new instance + var instance = new TraceSpan(); + + + if (data.TryGetValue("name", out var nameValue) && nameValue is not null) + { + instance.Name = nameValue?.ToString()!; + } + + if (data.TryGetValue("__time", out var __timeValue) && __timeValue is not null) + { + instance._Time = TraceTime.Load(__timeValue.GetDictionary(TraceTime.ShorthandProperty), context); + } + + if (data.TryGetValue("signature", out var signatureValue) && signatureValue is not null) + { + instance.Signature = signatureValue?.ToString()!; + } + + if (data.TryGetValue("inputs", out var inputsValue) && inputsValue is not null) + { + instance.Inputs = inputsValue.GetDictionary()!; + } + + if (data.TryGetValue("output", out var outputValue) && outputValue is not null) + { + instance.Output = outputValue; + } + + if (data.TryGetValue("error", out var errorValue) && errorValue is not null) + { + instance.Error = errorValue?.ToString()!; + } + + if (data.TryGetValue("__usage", out var __usageValue) && __usageValue is not null) + { + instance._Usage = TokenUsage.Load(__usageValue.GetDictionary(TokenUsage.ShorthandProperty), context); + } + + if (data.TryGetValue("attributes", out var attributesValue) && attributesValue is not null) + { + instance.Attributes = attributesValue.GetDictionary()!; + } + + if (data.TryGetValue("__frames", out var __framesValue) && __framesValue is not null) + { + instance._Frames = (__framesValue as IEnumerable)?.ToList() ?? []; + } + + if (context is not null) + { + instance = context.ProcessOutput(instance); + } + return instance; + } + + + #endregion + + #region Save Methods + + /// + /// Save the TraceSpan instance to a dictionary. + /// + /// Optional context with pre/post processing callbacks. + /// The dictionary representation of this instance. + public Dictionary Save(SaveContext? context = null) + { + var obj = this; + if (context is not null) + { + obj = context.ProcessObject(obj); + } + + + var result = new Dictionary(); + + + result["name"] = obj.Name; + + + result["__time"] = obj._Time?.Save(context); + + + if (obj.Signature is not null) + { + result["signature"] = obj.Signature; + } + + + if (obj.Inputs is not null) + { + result["inputs"] = obj.Inputs; + } + + + if (obj.Output is not null) + { + result["output"] = obj.Output; + } + + + if (obj.Error is not null) + { + result["error"] = obj.Error; + } + + + if (obj._Usage is not null) + { + result["__usage"] = obj._Usage?.Save(context); + } + + + if (obj.Attributes is not null) + { + result["attributes"] = obj.Attributes; + } + + + if (obj._Frames is not null) + { + result["__frames"] = obj._Frames; + } + + + if (context is not null) + { + result = context.ProcessDict(result); + } + + return result; + } + + + /// + /// Convert the TraceSpan instance to a YAML string. + /// + /// Optional context with pre/post processing callbacks. + /// The YAML string representation of this instance. + public string ToYaml(SaveContext? context = null) + { + context ??= new SaveContext(); + return context.ToYaml(Save(context)); + } + + /// + /// Convert the TraceSpan instance to a JSON string. + /// + /// Optional context with pre/post processing callbacks. + /// Whether to indent the output. Defaults to true. + /// The JSON string representation of this instance. + public string ToJson(SaveContext? context = null, bool indent = true) + { + context ??= new SaveContext(); + return context.ToJson(Save(context), indent); + } + + /// + /// Load a TraceSpan instance from a JSON string. + /// + /// The JSON string to parse. + /// Optional context with pre/post processing callbacks. + /// The loaded TraceSpan instance. + public static TraceSpan FromJson(string json, LoadContext? context = null) + { + using var doc = JsonDocument.Parse(json); + Dictionary dict; + dict = JsonSerializer.Deserialize>(json, JsonUtils.Options) + ?? throw new ArgumentException("Failed to parse JSON as dictionary"); + + return Load(dict, context); + } + + /// + /// Load a TraceSpan instance from a YAML string. + /// + /// The YAML string to parse. + /// Optional context with pre/post processing callbacks. + /// The loaded TraceSpan instance. + public static TraceSpan FromYaml(string yaml, LoadContext? context = null) + { + var dict = YamlUtils.Deserializer.Deserialize>(yaml) + ?? throw new ArgumentException("Failed to parse YAML as dictionary"); + + return Load(dict, context); + } + + #endregion +} diff --git a/runtime/csharp/Prompty.Core/Model/tracing/TraceTime.cs b/runtime/csharp/Prompty.Core/Model/tracing/TraceTime.cs new file mode 100644 index 00000000..a33015c5 --- /dev/null +++ b/runtime/csharp/Prompty.Core/Model/tracing/TraceTime.cs @@ -0,0 +1,181 @@ +// Copyright (c) Microsoft. All rights reserved. +using System.Text.Json; +using YamlDotNet.Serialization; + +#pragma warning disable IDE0130 +namespace Prompty.Core; +#pragma warning restore IDE0130 + +/// +/// Timing information for a trace span. +/// +public partial class TraceTime +{ + /// + /// The shorthand property name for this type, if any. + /// + public static string? ShorthandProperty => null; + + /// + /// Initializes a new instance of . + /// +#pragma warning disable CS8618 + public TraceTime() + { + } +#pragma warning restore CS8618 + + /// + /// ISO 8601 UTC timestamp when the span started + /// + public string Start { get; set; } = string.Empty; + + /// + /// ISO 8601 UTC timestamp when the span ended + /// + public string End { get; set; } = string.Empty; + + /// + /// Duration of the span in milliseconds + /// + public double Duration { get; set; } + + + + #region Load Methods + + /// + /// Load a TraceTime instance from a dictionary. + /// + /// The dictionary containing the data. + /// Optional context with pre/post processing callbacks. + /// The loaded TraceTime instance. + public static TraceTime Load(Dictionary data, LoadContext? context = null) + { + if (context is not null) + { + data = context.ProcessInput(data); + } + + + // Create new instance + var instance = new TraceTime(); + + + if (data.TryGetValue("start", out var startValue) && startValue is not null) + { + instance.Start = startValue?.ToString()!; + } + + if (data.TryGetValue("end", out var endValue) && endValue is not null) + { + instance.End = endValue?.ToString()!; + } + + if (data.TryGetValue("duration", out var durationValue) && durationValue is not null) + { + instance.Duration = Convert.ToDouble(durationValue); + } + + if (context is not null) + { + instance = context.ProcessOutput(instance); + } + return instance; + } + + + #endregion + + #region Save Methods + + /// + /// Save the TraceTime instance to a dictionary. + /// + /// Optional context with pre/post processing callbacks. + /// The dictionary representation of this instance. + public Dictionary Save(SaveContext? context = null) + { + var obj = this; + if (context is not null) + { + obj = context.ProcessObject(obj); + } + + + var result = new Dictionary(); + + + result["start"] = obj.Start; + + + result["end"] = obj.End; + + + result["duration"] = obj.Duration; + + + if (context is not null) + { + result = context.ProcessDict(result); + } + + return result; + } + + + /// + /// Convert the TraceTime instance to a YAML string. + /// + /// Optional context with pre/post processing callbacks. + /// The YAML string representation of this instance. + public string ToYaml(SaveContext? context = null) + { + context ??= new SaveContext(); + return context.ToYaml(Save(context)); + } + + /// + /// Convert the TraceTime instance to a JSON string. + /// + /// Optional context with pre/post processing callbacks. + /// Whether to indent the output. Defaults to true. + /// The JSON string representation of this instance. + public string ToJson(SaveContext? context = null, bool indent = true) + { + context ??= new SaveContext(); + return context.ToJson(Save(context), indent); + } + + /// + /// Load a TraceTime instance from a JSON string. + /// + /// The JSON string to parse. + /// Optional context with pre/post processing callbacks. + /// The loaded TraceTime instance. + public static TraceTime FromJson(string json, LoadContext? context = null) + { + using var doc = JsonDocument.Parse(json); + Dictionary dict; + dict = JsonSerializer.Deserialize>(json, JsonUtils.Options) + ?? throw new ArgumentException("Failed to parse JSON as dictionary"); + + return Load(dict, context); + } + + /// + /// Load a TraceTime instance from a YAML string. + /// + /// The YAML string to parse. + /// Optional context with pre/post processing callbacks. + /// The loaded TraceTime instance. + public static TraceTime FromYaml(string yaml, LoadContext? context = null) + { + var dict = YamlUtils.Deserializer.Deserialize>(yaml) + ?? throw new ArgumentException("Failed to parse YAML as dictionary"); + + return Load(dict, context); + } + + #endregion +} diff --git a/runtime/csharp/Prompty.Core/Model/wire/AnthropicImageBlock.cs b/runtime/csharp/Prompty.Core/Model/wire/AnthropicImageBlock.cs new file mode 100644 index 00000000..cb38ff9f --- /dev/null +++ b/runtime/csharp/Prompty.Core/Model/wire/AnthropicImageBlock.cs @@ -0,0 +1,170 @@ +// Copyright (c) Microsoft. All rights reserved. +using System.Text.Json; +using YamlDotNet.Serialization; + +#pragma warning disable IDE0130 +namespace Prompty.Core; +#pragma warning restore IDE0130 + +/// +/// An image content block using base64-encoded data. +/// +/// Anthropic requires images as base64 with an explicit media type. +/// +public partial class AnthropicImageBlock +{ + /// + /// The shorthand property name for this type, if any. + /// + public static string? ShorthandProperty => null; + + /// + /// Initializes a new instance of . + /// +#pragma warning disable CS8618 + public AnthropicImageBlock() + { + } +#pragma warning restore CS8618 + + /// + /// The content block type + /// + public string Type { get; set; } = "image"; + + /// + /// The image source (base64-encoded) + /// + public AnthropicImageSource Source { get; set; } + + + + #region Load Methods + + /// + /// Load a AnthropicImageBlock instance from a dictionary. + /// + /// The dictionary containing the data. + /// Optional context with pre/post processing callbacks. + /// The loaded AnthropicImageBlock instance. + public static AnthropicImageBlock Load(Dictionary data, LoadContext? context = null) + { + if (context is not null) + { + data = context.ProcessInput(data); + } + + + // Create new instance + var instance = new AnthropicImageBlock(); + + + if (data.TryGetValue("type", out var typeValue) && typeValue is not null) + { + instance.Type = typeValue?.ToString()!; + } + + if (data.TryGetValue("source", out var sourceValue) && sourceValue is not null) + { + instance.Source = AnthropicImageSource.Load(sourceValue.GetDictionary(AnthropicImageSource.ShorthandProperty), context); + } + + if (context is not null) + { + instance = context.ProcessOutput(instance); + } + return instance; + } + + + #endregion + + #region Save Methods + + /// + /// Save the AnthropicImageBlock instance to a dictionary. + /// + /// Optional context with pre/post processing callbacks. + /// The dictionary representation of this instance. + public Dictionary Save(SaveContext? context = null) + { + var obj = this; + if (context is not null) + { + obj = context.ProcessObject(obj); + } + + + var result = new Dictionary(); + + + result["type"] = obj.Type; + + + result["source"] = obj.Source?.Save(context); + + + if (context is not null) + { + result = context.ProcessDict(result); + } + + return result; + } + + + /// + /// Convert the AnthropicImageBlock instance to a YAML string. + /// + /// Optional context with pre/post processing callbacks. + /// The YAML string representation of this instance. + public string ToYaml(SaveContext? context = null) + { + context ??= new SaveContext(); + return context.ToYaml(Save(context)); + } + + /// + /// Convert the AnthropicImageBlock instance to a JSON string. + /// + /// Optional context with pre/post processing callbacks. + /// Whether to indent the output. Defaults to true. + /// The JSON string representation of this instance. + public string ToJson(SaveContext? context = null, bool indent = true) + { + context ??= new SaveContext(); + return context.ToJson(Save(context), indent); + } + + /// + /// Load a AnthropicImageBlock instance from a JSON string. + /// + /// The JSON string to parse. + /// Optional context with pre/post processing callbacks. + /// The loaded AnthropicImageBlock instance. + public static AnthropicImageBlock FromJson(string json, LoadContext? context = null) + { + using var doc = JsonDocument.Parse(json); + Dictionary dict; + dict = JsonSerializer.Deserialize>(json, JsonUtils.Options) + ?? throw new ArgumentException("Failed to parse JSON as dictionary"); + + return Load(dict, context); + } + + /// + /// Load a AnthropicImageBlock instance from a YAML string. + /// + /// The YAML string to parse. + /// Optional context with pre/post processing callbacks. + /// The loaded AnthropicImageBlock instance. + public static AnthropicImageBlock FromYaml(string yaml, LoadContext? context = null) + { + var dict = YamlUtils.Deserializer.Deserialize>(yaml) + ?? throw new ArgumentException("Failed to parse YAML as dictionary"); + + return Load(dict, context); + } + + #endregion +} diff --git a/runtime/csharp/Prompty.Core/Model/wire/AnthropicImageSource.cs b/runtime/csharp/Prompty.Core/Model/wire/AnthropicImageSource.cs new file mode 100644 index 00000000..70daff2d --- /dev/null +++ b/runtime/csharp/Prompty.Core/Model/wire/AnthropicImageSource.cs @@ -0,0 +1,181 @@ +// Copyright (c) Microsoft. All rights reserved. +using System.Text.Json; +using YamlDotNet.Serialization; + +#pragma warning disable IDE0130 +namespace Prompty.Core; +#pragma warning restore IDE0130 + +/// +/// Source descriptor for an Anthropic base64 image. +/// +public partial class AnthropicImageSource +{ + /// + /// The shorthand property name for this type, if any. + /// + public static string? ShorthandProperty => null; + + /// + /// Initializes a new instance of . + /// +#pragma warning disable CS8618 + public AnthropicImageSource() + { + } +#pragma warning restore CS8618 + + /// + /// The encoding type (always 'base64' for inline images) + /// + public string Type { get; set; } = "base64"; + + /// + /// The MIME type of the image (e.g., 'image/png', 'image/jpeg') + /// + public string MediaType { get; set; } = string.Empty; + + /// + /// The base64-encoded image data + /// + public string Data { get; set; } = string.Empty; + + + + #region Load Methods + + /// + /// Load a AnthropicImageSource instance from a dictionary. + /// + /// The dictionary containing the data. + /// Optional context with pre/post processing callbacks. + /// The loaded AnthropicImageSource instance. + public static AnthropicImageSource Load(Dictionary data, LoadContext? context = null) + { + if (context is not null) + { + data = context.ProcessInput(data); + } + + + // Create new instance + var instance = new AnthropicImageSource(); + + + if (data.TryGetValue("type", out var typeValue) && typeValue is not null) + { + instance.Type = typeValue?.ToString()!; + } + + if (data.TryGetValue("media_type", out var media_typeValue) && media_typeValue is not null) + { + instance.MediaType = media_typeValue?.ToString()!; + } + + if (data.TryGetValue("data", out var dataValue) && dataValue is not null) + { + instance.Data = dataValue?.ToString()!; + } + + if (context is not null) + { + instance = context.ProcessOutput(instance); + } + return instance; + } + + + #endregion + + #region Save Methods + + /// + /// Save the AnthropicImageSource instance to a dictionary. + /// + /// Optional context with pre/post processing callbacks. + /// The dictionary representation of this instance. + public Dictionary Save(SaveContext? context = null) + { + var obj = this; + if (context is not null) + { + obj = context.ProcessObject(obj); + } + + + var result = new Dictionary(); + + + result["type"] = obj.Type; + + + result["media_type"] = obj.MediaType; + + + result["data"] = obj.Data; + + + if (context is not null) + { + result = context.ProcessDict(result); + } + + return result; + } + + + /// + /// Convert the AnthropicImageSource instance to a YAML string. + /// + /// Optional context with pre/post processing callbacks. + /// The YAML string representation of this instance. + public string ToYaml(SaveContext? context = null) + { + context ??= new SaveContext(); + return context.ToYaml(Save(context)); + } + + /// + /// Convert the AnthropicImageSource instance to a JSON string. + /// + /// Optional context with pre/post processing callbacks. + /// Whether to indent the output. Defaults to true. + /// The JSON string representation of this instance. + public string ToJson(SaveContext? context = null, bool indent = true) + { + context ??= new SaveContext(); + return context.ToJson(Save(context), indent); + } + + /// + /// Load a AnthropicImageSource instance from a JSON string. + /// + /// The JSON string to parse. + /// Optional context with pre/post processing callbacks. + /// The loaded AnthropicImageSource instance. + public static AnthropicImageSource FromJson(string json, LoadContext? context = null) + { + using var doc = JsonDocument.Parse(json); + Dictionary dict; + dict = JsonSerializer.Deserialize>(json, JsonUtils.Options) + ?? throw new ArgumentException("Failed to parse JSON as dictionary"); + + return Load(dict, context); + } + + /// + /// Load a AnthropicImageSource instance from a YAML string. + /// + /// The YAML string to parse. + /// Optional context with pre/post processing callbacks. + /// The loaded AnthropicImageSource instance. + public static AnthropicImageSource FromYaml(string yaml, LoadContext? context = null) + { + var dict = YamlUtils.Deserializer.Deserialize>(yaml) + ?? throw new ArgumentException("Failed to parse YAML as dictionary"); + + return Load(dict, context); + } + + #endregion +} diff --git a/runtime/csharp/Prompty.Core/Model/wire/AnthropicMessagesRequest.cs b/runtime/csharp/Prompty.Core/Model/wire/AnthropicMessagesRequest.cs new file mode 100644 index 00000000..d17002d2 --- /dev/null +++ b/runtime/csharp/Prompty.Core/Model/wire/AnthropicMessagesRequest.cs @@ -0,0 +1,442 @@ +// Copyright (c) Microsoft. All rights reserved. +using System.Text.Json; +using YamlDotNet.Serialization; + +#pragma warning disable IDE0130 +namespace Prompty.Core; +#pragma warning restore IDE0130 + +/// +/// The full request body for the Anthropic Messages API (§7.5). +/// +public partial class AnthropicMessagesRequest +{ + /// + /// The shorthand property name for this type, if any. + /// + public static string? ShorthandProperty => null; + + /// + /// Initializes a new instance of . + /// +#pragma warning disable CS8618 + public AnthropicMessagesRequest() + { + } +#pragma warning restore CS8618 + + /// + /// The model identifier + /// + public string Model { get; set; } = string.Empty; + + /// + /// The non-system messages to send + /// + public IList Messages { get; set; } = []; + + /// + /// Maximum number of tokens to generate (required by Anthropic) + /// + public int MaxTokens { get; set; } + + /// + /// System prompt text (extracted from system-role messages) + /// + public string? System { get; set; } + + /// + /// Sampling temperature + /// + public float? Temperature { get; set; } + + /// + /// Top-P sampling value + /// + public float? TopP { get; set; } + + /// + /// Top-K sampling value + /// + public int? TopK { get; set; } + + /// + /// Stop sequences to end generation + /// + public IList? StopSequences { get; set; } + + /// + /// Tool definitions available to the model + /// + public IList? Tools { get; set; } + + + + #region Load Methods + + /// + /// Load a AnthropicMessagesRequest instance from a dictionary. + /// + /// The dictionary containing the data. + /// Optional context with pre/post processing callbacks. + /// The loaded AnthropicMessagesRequest instance. + public static AnthropicMessagesRequest Load(Dictionary data, LoadContext? context = null) + { + if (context is not null) + { + data = context.ProcessInput(data); + } + + + // Create new instance + var instance = new AnthropicMessagesRequest(); + + + if (data.TryGetValue("model", out var modelValue) && modelValue is not null) + { + instance.Model = modelValue?.ToString()!; + } + + if (data.TryGetValue("messages", out var messagesValue) && messagesValue is not null) + { + instance.Messages = LoadMessages(messagesValue, context); + } + + if (data.TryGetValue("max_tokens", out var max_tokensValue) && max_tokensValue is not null) + { + instance.MaxTokens = Convert.ToInt32(max_tokensValue); + } + + if (data.TryGetValue("system", out var systemValue) && systemValue is not null) + { + instance.System = systemValue?.ToString()!; + } + + if (data.TryGetValue("temperature", out var temperatureValue) && temperatureValue is not null) + { + instance.Temperature = Convert.ToSingle(temperatureValue); + } + + if (data.TryGetValue("top_p", out var top_pValue) && top_pValue is not null) + { + instance.TopP = Convert.ToSingle(top_pValue); + } + + if (data.TryGetValue("top_k", out var top_kValue) && top_kValue is not null) + { + instance.TopK = Convert.ToInt32(top_kValue); + } + + if (data.TryGetValue("stop_sequences", out var stop_sequencesValue) && stop_sequencesValue is not null) + { + instance.StopSequences = (stop_sequencesValue as IEnumerable)?.Select(x => x?.ToString()!).ToList() ?? []; + } + + if (data.TryGetValue("tools", out var toolsValue) && toolsValue is not null) + { + instance.Tools = LoadTools(toolsValue, context); + } + + if (context is not null) + { + instance = context.ProcessOutput(instance); + } + return instance; + } + + + /// + /// Load a list of AnthropicWireMessage from a dictionary or list. + /// + public static IList LoadMessages(object data, LoadContext? context) + { + var result = new List(); + + if (data is Dictionary dict) + { + // Convert named dictionary to list + foreach (var kvp in dict) + { + if (kvp.Value is IEnumerable) + { + throw new ArgumentException( + $"Invalid 'messages' format: key '{kvp.Key}' has an array value. " + + $"'messages' must be a flat list of objects or a name-keyed dict — " + + "not a nested {" + kvp.Key + ": [...]} structure."); + } + var itemDict = kvp.Value.GetDictionary(); + if (itemDict.Count > 0) + { + // Value is an object, add name to it + itemDict["name"] = kvp.Key; + result.Add(AnthropicWireMessage.Load(itemDict, context)); + } + else + { + // Value is a scalar, use it as the primary property + var newDict = new Dictionary + { + ["name"] = kvp.Key, + ["role"] = kvp.Value + }; + result.Add(AnthropicWireMessage.Load(newDict, context)); + } + } + } + else if (data is IEnumerable list) + { + foreach (var item in list) + { + var itemDict = item.GetDictionary(AnthropicWireMessage.ShorthandProperty); + if (itemDict.Count > 0) + { + result.Add(AnthropicWireMessage.Load(itemDict, context)); + } + } + } + + return result; + } + + + /// + /// Load a list of AnthropicToolDefinition from a dictionary or list. + /// + public static IList LoadTools(object data, LoadContext? context) + { + var result = new List(); + + if (data is Dictionary dict) + { + // Convert named dictionary to list + foreach (var kvp in dict) + { + if (kvp.Value is IEnumerable) + { + throw new ArgumentException( + $"Invalid 'tools' format: key '{kvp.Key}' has an array value. " + + $"'tools' must be a flat list of objects or a name-keyed dict — " + + "not a nested {" + kvp.Key + ": [...]} structure."); + } + var itemDict = kvp.Value.GetDictionary(); + if (itemDict.Count > 0) + { + // Value is an object, add name to it + itemDict["name"] = kvp.Key; + result.Add(AnthropicToolDefinition.Load(itemDict, context)); + } + else + { + // Value is a scalar, use it as the primary property + var newDict = new Dictionary + { + ["name"] = kvp.Key, + ["description"] = kvp.Value + }; + result.Add(AnthropicToolDefinition.Load(newDict, context)); + } + } + } + else if (data is IEnumerable list) + { + foreach (var item in list) + { + var itemDict = item.GetDictionary(AnthropicToolDefinition.ShorthandProperty); + if (itemDict.Count > 0) + { + result.Add(AnthropicToolDefinition.Load(itemDict, context)); + } + } + } + + return result; + } + + + #endregion + + #region Save Methods + + /// + /// Save the AnthropicMessagesRequest instance to a dictionary. + /// + /// Optional context with pre/post processing callbacks. + /// The dictionary representation of this instance. + public Dictionary Save(SaveContext? context = null) + { + var obj = this; + if (context is not null) + { + obj = context.ProcessObject(obj); + } + + + var result = new Dictionary(); + + + result["model"] = obj.Model; + + + result["messages"] = SaveMessages(obj.Messages, context); + + + result["max_tokens"] = obj.MaxTokens; + + + if (obj.System is not null) + { + result["system"] = obj.System; + } + + + if (obj.Temperature is not null) + { + result["temperature"] = obj.Temperature; + } + + + if (obj.TopP is not null) + { + result["top_p"] = obj.TopP; + } + + + if (obj.TopK is not null) + { + result["top_k"] = obj.TopK; + } + + + if (obj.StopSequences is not null) + { + result["stop_sequences"] = obj.StopSequences; + } + + + if (obj.Tools is not null) + { + result["tools"] = SaveTools(obj.Tools, context); + } + + + if (context is not null) + { + result = context.ProcessDict(result); + } + + return result; + } + + + /// + /// Save a list of AnthropicWireMessage to object or array format. + /// + public static object SaveMessages(IList items, SaveContext? context) + { + context ??= new SaveContext(); + + // This collection type does not have a 'name' property, only array format is supported + return items.Select(item => item.Save(context)).ToList(); + + } + + + /// + /// Save a list of AnthropicToolDefinition to object or array format. + /// + public static object SaveTools(IList items, SaveContext? context) + { + context ??= new SaveContext(); + + + if (context.CollectionFormat == "array") + { + return items.Select(item => item.Save(context)).ToList(); + } + + // Object format: use name as key + var result = new Dictionary(); + foreach (var item in items) + { + var itemData = item.Save(context); + if (itemData.TryGetValue("name", out var nameValue) && nameValue is string name) + { + itemData.Remove("name"); + + // Check if we can use shorthand + if (context.UseShorthand && AnthropicToolDefinition.ShorthandProperty is string shorthandProp) + { + if (itemData.Count == 1 && itemData.ContainsKey(shorthandProp)) + { + result[name] = itemData[shorthandProp]; + continue; + } + } + result[name] = itemData; + } + else + { + // No name, can't use object format for this item + throw new InvalidOperationException("Cannot save item in object format: missing 'name' property"); + } + } + return result; + + } + + + /// + /// Convert the AnthropicMessagesRequest instance to a YAML string. + /// + /// Optional context with pre/post processing callbacks. + /// The YAML string representation of this instance. + public string ToYaml(SaveContext? context = null) + { + context ??= new SaveContext(); + return context.ToYaml(Save(context)); + } + + /// + /// Convert the AnthropicMessagesRequest instance to a JSON string. + /// + /// Optional context with pre/post processing callbacks. + /// Whether to indent the output. Defaults to true. + /// The JSON string representation of this instance. + public string ToJson(SaveContext? context = null, bool indent = true) + { + context ??= new SaveContext(); + return context.ToJson(Save(context), indent); + } + + /// + /// Load a AnthropicMessagesRequest instance from a JSON string. + /// + /// The JSON string to parse. + /// Optional context with pre/post processing callbacks. + /// The loaded AnthropicMessagesRequest instance. + public static AnthropicMessagesRequest FromJson(string json, LoadContext? context = null) + { + using var doc = JsonDocument.Parse(json); + Dictionary dict; + dict = JsonSerializer.Deserialize>(json, JsonUtils.Options) + ?? throw new ArgumentException("Failed to parse JSON as dictionary"); + + return Load(dict, context); + } + + /// + /// Load a AnthropicMessagesRequest instance from a YAML string. + /// + /// The YAML string to parse. + /// Optional context with pre/post processing callbacks. + /// The loaded AnthropicMessagesRequest instance. + public static AnthropicMessagesRequest FromYaml(string yaml, LoadContext? context = null) + { + var dict = YamlUtils.Deserializer.Deserialize>(yaml) + ?? throw new ArgumentException("Failed to parse YAML as dictionary"); + + return Load(dict, context); + } + + #endregion +} diff --git a/runtime/csharp/Prompty.Core/Model/wire/AnthropicMessagesResponse.cs b/runtime/csharp/Prompty.Core/Model/wire/AnthropicMessagesResponse.cs new file mode 100644 index 00000000..5fce011e --- /dev/null +++ b/runtime/csharp/Prompty.Core/Model/wire/AnthropicMessagesResponse.cs @@ -0,0 +1,233 @@ +// Copyright (c) Microsoft. All rights reserved. +using System.Text.Json; +using YamlDotNet.Serialization; + +#pragma warning disable IDE0130 +namespace Prompty.Core; +#pragma warning restore IDE0130 + +/// +/// The response body from the Anthropic Messages API. +/// +public partial class AnthropicMessagesResponse +{ + /// + /// The shorthand property name for this type, if any. + /// + public static string? ShorthandProperty => null; + + /// + /// Initializes a new instance of . + /// +#pragma warning disable CS8618 + public AnthropicMessagesResponse() + { + } +#pragma warning restore CS8618 + + /// + /// Unique response identifier + /// + public string Id { get; set; } = string.Empty; + + /// + /// Object type (always 'message') + /// + public string Type { get; set; } = "message"; + + /// + /// The role of the response (always 'assistant') + /// + public string Role { get; set; } = "assistant"; + + /// + /// Array of content blocks in the response (AnthropicTextBlock | AnthropicToolUseBlock) + /// + public IList Content { get; set; } = []; + + /// + /// The model that generated the response + /// + public string Model { get; set; } = string.Empty; + + /// + /// The reason generation stopped ('end_turn', 'max_tokens', 'stop_sequence', 'tool_use') + /// + public string StopReason { get; set; } = string.Empty; + + /// + /// Token usage statistics + /// + public AnthropicUsage Usage { get; set; } + + + + #region Load Methods + + /// + /// Load a AnthropicMessagesResponse instance from a dictionary. + /// + /// The dictionary containing the data. + /// Optional context with pre/post processing callbacks. + /// The loaded AnthropicMessagesResponse instance. + public static AnthropicMessagesResponse Load(Dictionary data, LoadContext? context = null) + { + if (context is not null) + { + data = context.ProcessInput(data); + } + + + // Create new instance + var instance = new AnthropicMessagesResponse(); + + + if (data.TryGetValue("id", out var idValue) && idValue is not null) + { + instance.Id = idValue?.ToString()!; + } + + if (data.TryGetValue("type", out var typeValue) && typeValue is not null) + { + instance.Type = typeValue?.ToString()!; + } + + if (data.TryGetValue("role", out var roleValue) && roleValue is not null) + { + instance.Role = roleValue?.ToString()!; + } + + if (data.TryGetValue("content", out var contentValue) && contentValue is not null) + { + instance.Content = (contentValue as IEnumerable)?.ToList() ?? []; + } + + if (data.TryGetValue("model", out var modelValue) && modelValue is not null) + { + instance.Model = modelValue?.ToString()!; + } + + if (data.TryGetValue("stop_reason", out var stop_reasonValue) && stop_reasonValue is not null) + { + instance.StopReason = stop_reasonValue?.ToString()!; + } + + if (data.TryGetValue("usage", out var usageValue) && usageValue is not null) + { + instance.Usage = AnthropicUsage.Load(usageValue.GetDictionary(AnthropicUsage.ShorthandProperty), context); + } + + if (context is not null) + { + instance = context.ProcessOutput(instance); + } + return instance; + } + + + #endregion + + #region Save Methods + + /// + /// Save the AnthropicMessagesResponse instance to a dictionary. + /// + /// Optional context with pre/post processing callbacks. + /// The dictionary representation of this instance. + public Dictionary Save(SaveContext? context = null) + { + var obj = this; + if (context is not null) + { + obj = context.ProcessObject(obj); + } + + + var result = new Dictionary(); + + + result["id"] = obj.Id; + + + result["type"] = obj.Type; + + + result["role"] = obj.Role; + + + result["content"] = obj.Content; + + + result["model"] = obj.Model; + + + result["stop_reason"] = obj.StopReason; + + + result["usage"] = obj.Usage?.Save(context); + + + if (context is not null) + { + result = context.ProcessDict(result); + } + + return result; + } + + + /// + /// Convert the AnthropicMessagesResponse instance to a YAML string. + /// + /// Optional context with pre/post processing callbacks. + /// The YAML string representation of this instance. + public string ToYaml(SaveContext? context = null) + { + context ??= new SaveContext(); + return context.ToYaml(Save(context)); + } + + /// + /// Convert the AnthropicMessagesResponse instance to a JSON string. + /// + /// Optional context with pre/post processing callbacks. + /// Whether to indent the output. Defaults to true. + /// The JSON string representation of this instance. + public string ToJson(SaveContext? context = null, bool indent = true) + { + context ??= new SaveContext(); + return context.ToJson(Save(context), indent); + } + + /// + /// Load a AnthropicMessagesResponse instance from a JSON string. + /// + /// The JSON string to parse. + /// Optional context with pre/post processing callbacks. + /// The loaded AnthropicMessagesResponse instance. + public static AnthropicMessagesResponse FromJson(string json, LoadContext? context = null) + { + using var doc = JsonDocument.Parse(json); + Dictionary dict; + dict = JsonSerializer.Deserialize>(json, JsonUtils.Options) + ?? throw new ArgumentException("Failed to parse JSON as dictionary"); + + return Load(dict, context); + } + + /// + /// Load a AnthropicMessagesResponse instance from a YAML string. + /// + /// The YAML string to parse. + /// Optional context with pre/post processing callbacks. + /// The loaded AnthropicMessagesResponse instance. + public static AnthropicMessagesResponse FromYaml(string yaml, LoadContext? context = null) + { + var dict = YamlUtils.Deserializer.Deserialize>(yaml) + ?? throw new ArgumentException("Failed to parse YAML as dictionary"); + + return Load(dict, context); + } + + #endregion +} diff --git a/runtime/csharp/Prompty.Core/Model/wire/AnthropicTextBlock.cs b/runtime/csharp/Prompty.Core/Model/wire/AnthropicTextBlock.cs new file mode 100644 index 00000000..2dd16faa --- /dev/null +++ b/runtime/csharp/Prompty.Core/Model/wire/AnthropicTextBlock.cs @@ -0,0 +1,168 @@ +// Copyright (c) Microsoft. All rights reserved. +using System.Text.Json; +using YamlDotNet.Serialization; + +#pragma warning disable IDE0130 +namespace Prompty.Core; +#pragma warning restore IDE0130 + +/// +/// A text content block in Anthropic's array-of-blocks message format. +/// +public partial class AnthropicTextBlock +{ + /// + /// The shorthand property name for this type, if any. + /// + public static string? ShorthandProperty => null; + + /// + /// Initializes a new instance of . + /// +#pragma warning disable CS8618 + public AnthropicTextBlock() + { + } +#pragma warning restore CS8618 + + /// + /// The content block type + /// + public string Type { get; set; } = "text"; + + /// + /// The text content + /// + public string Text { get; set; } = string.Empty; + + + + #region Load Methods + + /// + /// Load a AnthropicTextBlock instance from a dictionary. + /// + /// The dictionary containing the data. + /// Optional context with pre/post processing callbacks. + /// The loaded AnthropicTextBlock instance. + public static AnthropicTextBlock Load(Dictionary data, LoadContext? context = null) + { + if (context is not null) + { + data = context.ProcessInput(data); + } + + + // Create new instance + var instance = new AnthropicTextBlock(); + + + if (data.TryGetValue("type", out var typeValue) && typeValue is not null) + { + instance.Type = typeValue?.ToString()!; + } + + if (data.TryGetValue("text", out var textValue) && textValue is not null) + { + instance.Text = textValue?.ToString()!; + } + + if (context is not null) + { + instance = context.ProcessOutput(instance); + } + return instance; + } + + + #endregion + + #region Save Methods + + /// + /// Save the AnthropicTextBlock instance to a dictionary. + /// + /// Optional context with pre/post processing callbacks. + /// The dictionary representation of this instance. + public Dictionary Save(SaveContext? context = null) + { + var obj = this; + if (context is not null) + { + obj = context.ProcessObject(obj); + } + + + var result = new Dictionary(); + + + result["type"] = obj.Type; + + + result["text"] = obj.Text; + + + if (context is not null) + { + result = context.ProcessDict(result); + } + + return result; + } + + + /// + /// Convert the AnthropicTextBlock instance to a YAML string. + /// + /// Optional context with pre/post processing callbacks. + /// The YAML string representation of this instance. + public string ToYaml(SaveContext? context = null) + { + context ??= new SaveContext(); + return context.ToYaml(Save(context)); + } + + /// + /// Convert the AnthropicTextBlock instance to a JSON string. + /// + /// Optional context with pre/post processing callbacks. + /// Whether to indent the output. Defaults to true. + /// The JSON string representation of this instance. + public string ToJson(SaveContext? context = null, bool indent = true) + { + context ??= new SaveContext(); + return context.ToJson(Save(context), indent); + } + + /// + /// Load a AnthropicTextBlock instance from a JSON string. + /// + /// The JSON string to parse. + /// Optional context with pre/post processing callbacks. + /// The loaded AnthropicTextBlock instance. + public static AnthropicTextBlock FromJson(string json, LoadContext? context = null) + { + using var doc = JsonDocument.Parse(json); + Dictionary dict; + dict = JsonSerializer.Deserialize>(json, JsonUtils.Options) + ?? throw new ArgumentException("Failed to parse JSON as dictionary"); + + return Load(dict, context); + } + + /// + /// Load a AnthropicTextBlock instance from a YAML string. + /// + /// The YAML string to parse. + /// Optional context with pre/post processing callbacks. + /// The loaded AnthropicTextBlock instance. + public static AnthropicTextBlock FromYaml(string yaml, LoadContext? context = null) + { + var dict = YamlUtils.Deserializer.Deserialize>(yaml) + ?? throw new ArgumentException("Failed to parse YAML as dictionary"); + + return Load(dict, context); + } + + #endregion +} diff --git a/runtime/csharp/Prompty.Core/Model/wire/AnthropicToolDefinition.cs b/runtime/csharp/Prompty.Core/Model/wire/AnthropicToolDefinition.cs new file mode 100644 index 00000000..8ccddd4f --- /dev/null +++ b/runtime/csharp/Prompty.Core/Model/wire/AnthropicToolDefinition.cs @@ -0,0 +1,188 @@ +// Copyright (c) Microsoft. All rights reserved. +using System.Text.Json; +using YamlDotNet.Serialization; + +#pragma warning disable IDE0130 +namespace Prompty.Core; +#pragma warning restore IDE0130 + +/// +/// A tool definition in Anthropic's format. Unlike OpenAI which wraps +/// +/// tools in `{type: "function", function: {...}}`, Anthropic uses a +/// +/// flat structure with `input_schema` (§7.5). +/// +public partial class AnthropicToolDefinition +{ + /// + /// The shorthand property name for this type, if any. + /// + public static string? ShorthandProperty => null; + + /// + /// Initializes a new instance of . + /// +#pragma warning disable CS8618 + public AnthropicToolDefinition() + { + } +#pragma warning restore CS8618 + + /// + /// The tool name + /// + public string Name { get; set; } = string.Empty; + + /// + /// A description of what the tool does + /// + public string? Description { get; set; } + + /// + /// JSON Schema describing the tool's input parameters + /// + public IDictionary InputSchema { get; set; } = new Dictionary(); + + + + #region Load Methods + + /// + /// Load a AnthropicToolDefinition instance from a dictionary. + /// + /// The dictionary containing the data. + /// Optional context with pre/post processing callbacks. + /// The loaded AnthropicToolDefinition instance. + public static AnthropicToolDefinition Load(Dictionary data, LoadContext? context = null) + { + if (context is not null) + { + data = context.ProcessInput(data); + } + + + // Create new instance + var instance = new AnthropicToolDefinition(); + + + if (data.TryGetValue("name", out var nameValue) && nameValue is not null) + { + instance.Name = nameValue?.ToString()!; + } + + if (data.TryGetValue("description", out var descriptionValue) && descriptionValue is not null) + { + instance.Description = descriptionValue?.ToString()!; + } + + if (data.TryGetValue("input_schema", out var input_schemaValue) && input_schemaValue is not null) + { + instance.InputSchema = input_schemaValue.GetDictionary()!; + } + + if (context is not null) + { + instance = context.ProcessOutput(instance); + } + return instance; + } + + + #endregion + + #region Save Methods + + /// + /// Save the AnthropicToolDefinition instance to a dictionary. + /// + /// Optional context with pre/post processing callbacks. + /// The dictionary representation of this instance. + public Dictionary Save(SaveContext? context = null) + { + var obj = this; + if (context is not null) + { + obj = context.ProcessObject(obj); + } + + + var result = new Dictionary(); + + + result["name"] = obj.Name; + + + if (obj.Description is not null) + { + result["description"] = obj.Description; + } + + + result["input_schema"] = obj.InputSchema; + + + if (context is not null) + { + result = context.ProcessDict(result); + } + + return result; + } + + + /// + /// Convert the AnthropicToolDefinition instance to a YAML string. + /// + /// Optional context with pre/post processing callbacks. + /// The YAML string representation of this instance. + public string ToYaml(SaveContext? context = null) + { + context ??= new SaveContext(); + return context.ToYaml(Save(context)); + } + + /// + /// Convert the AnthropicToolDefinition instance to a JSON string. + /// + /// Optional context with pre/post processing callbacks. + /// Whether to indent the output. Defaults to true. + /// The JSON string representation of this instance. + public string ToJson(SaveContext? context = null, bool indent = true) + { + context ??= new SaveContext(); + return context.ToJson(Save(context), indent); + } + + /// + /// Load a AnthropicToolDefinition instance from a JSON string. + /// + /// The JSON string to parse. + /// Optional context with pre/post processing callbacks. + /// The loaded AnthropicToolDefinition instance. + public static AnthropicToolDefinition FromJson(string json, LoadContext? context = null) + { + using var doc = JsonDocument.Parse(json); + Dictionary dict; + dict = JsonSerializer.Deserialize>(json, JsonUtils.Options) + ?? throw new ArgumentException("Failed to parse JSON as dictionary"); + + return Load(dict, context); + } + + /// + /// Load a AnthropicToolDefinition instance from a YAML string. + /// + /// The YAML string to parse. + /// Optional context with pre/post processing callbacks. + /// The loaded AnthropicToolDefinition instance. + public static AnthropicToolDefinition FromYaml(string yaml, LoadContext? context = null) + { + var dict = YamlUtils.Deserializer.Deserialize>(yaml) + ?? throw new ArgumentException("Failed to parse YAML as dictionary"); + + return Load(dict, context); + } + + #endregion +} diff --git a/runtime/csharp/Prompty.Core/Model/wire/AnthropicToolResultBlock.cs b/runtime/csharp/Prompty.Core/Model/wire/AnthropicToolResultBlock.cs new file mode 100644 index 00000000..7a2d9f97 --- /dev/null +++ b/runtime/csharp/Prompty.Core/Model/wire/AnthropicToolResultBlock.cs @@ -0,0 +1,181 @@ +// Copyright (c) Microsoft. All rights reserved. +using System.Text.Json; +using YamlDotNet.Serialization; + +#pragma warning disable IDE0130 +namespace Prompty.Core; +#pragma warning restore IDE0130 + +/// +/// A tool result content block sent back to the API with the tool's output. +/// +public partial class AnthropicToolResultBlock +{ + /// + /// The shorthand property name for this type, if any. + /// + public static string? ShorthandProperty => null; + + /// + /// Initializes a new instance of . + /// +#pragma warning disable CS8618 + public AnthropicToolResultBlock() + { + } +#pragma warning restore CS8618 + + /// + /// The content block type + /// + public string Type { get; set; } = "tool_result"; + + /// + /// The tool_use id this result corresponds to + /// + public string ToolUseId { get; set; } = string.Empty; + + /// + /// The tool's output content + /// + public string Content { get; set; } = string.Empty; + + + + #region Load Methods + + /// + /// Load a AnthropicToolResultBlock instance from a dictionary. + /// + /// The dictionary containing the data. + /// Optional context with pre/post processing callbacks. + /// The loaded AnthropicToolResultBlock instance. + public static AnthropicToolResultBlock Load(Dictionary data, LoadContext? context = null) + { + if (context is not null) + { + data = context.ProcessInput(data); + } + + + // Create new instance + var instance = new AnthropicToolResultBlock(); + + + if (data.TryGetValue("type", out var typeValue) && typeValue is not null) + { + instance.Type = typeValue?.ToString()!; + } + + if (data.TryGetValue("tool_use_id", out var tool_use_idValue) && tool_use_idValue is not null) + { + instance.ToolUseId = tool_use_idValue?.ToString()!; + } + + if (data.TryGetValue("content", out var contentValue) && contentValue is not null) + { + instance.Content = contentValue?.ToString()!; + } + + if (context is not null) + { + instance = context.ProcessOutput(instance); + } + return instance; + } + + + #endregion + + #region Save Methods + + /// + /// Save the AnthropicToolResultBlock instance to a dictionary. + /// + /// Optional context with pre/post processing callbacks. + /// The dictionary representation of this instance. + public Dictionary Save(SaveContext? context = null) + { + var obj = this; + if (context is not null) + { + obj = context.ProcessObject(obj); + } + + + var result = new Dictionary(); + + + result["type"] = obj.Type; + + + result["tool_use_id"] = obj.ToolUseId; + + + result["content"] = obj.Content; + + + if (context is not null) + { + result = context.ProcessDict(result); + } + + return result; + } + + + /// + /// Convert the AnthropicToolResultBlock instance to a YAML string. + /// + /// Optional context with pre/post processing callbacks. + /// The YAML string representation of this instance. + public string ToYaml(SaveContext? context = null) + { + context ??= new SaveContext(); + return context.ToYaml(Save(context)); + } + + /// + /// Convert the AnthropicToolResultBlock instance to a JSON string. + /// + /// Optional context with pre/post processing callbacks. + /// Whether to indent the output. Defaults to true. + /// The JSON string representation of this instance. + public string ToJson(SaveContext? context = null, bool indent = true) + { + context ??= new SaveContext(); + return context.ToJson(Save(context), indent); + } + + /// + /// Load a AnthropicToolResultBlock instance from a JSON string. + /// + /// The JSON string to parse. + /// Optional context with pre/post processing callbacks. + /// The loaded AnthropicToolResultBlock instance. + public static AnthropicToolResultBlock FromJson(string json, LoadContext? context = null) + { + using var doc = JsonDocument.Parse(json); + Dictionary dict; + dict = JsonSerializer.Deserialize>(json, JsonUtils.Options) + ?? throw new ArgumentException("Failed to parse JSON as dictionary"); + + return Load(dict, context); + } + + /// + /// Load a AnthropicToolResultBlock instance from a YAML string. + /// + /// The YAML string to parse. + /// Optional context with pre/post processing callbacks. + /// The loaded AnthropicToolResultBlock instance. + public static AnthropicToolResultBlock FromYaml(string yaml, LoadContext? context = null) + { + var dict = YamlUtils.Deserializer.Deserialize>(yaml) + ?? throw new ArgumentException("Failed to parse YAML as dictionary"); + + return Load(dict, context); + } + + #endregion +} diff --git a/runtime/csharp/Prompty.Core/Model/wire/AnthropicToolUseBlock.cs b/runtime/csharp/Prompty.Core/Model/wire/AnthropicToolUseBlock.cs new file mode 100644 index 00000000..74d9e54b --- /dev/null +++ b/runtime/csharp/Prompty.Core/Model/wire/AnthropicToolUseBlock.cs @@ -0,0 +1,196 @@ +// Copyright (c) Microsoft. All rights reserved. +using System.Text.Json; +using YamlDotNet.Serialization; + +#pragma warning disable IDE0130 +namespace Prompty.Core; +#pragma warning restore IDE0130 + +/// +/// A tool use content block returned in an assistant message when +/// +/// the model wants to invoke a tool. +/// +public partial class AnthropicToolUseBlock +{ + /// + /// The shorthand property name for this type, if any. + /// + public static string? ShorthandProperty => null; + + /// + /// Initializes a new instance of . + /// +#pragma warning disable CS8618 + public AnthropicToolUseBlock() + { + } +#pragma warning restore CS8618 + + /// + /// The content block type + /// + public string Type { get; set; } = "tool_use"; + + /// + /// Unique identifier for this tool invocation + /// + public string Id { get; set; } = string.Empty; + + /// + /// The name of the tool to invoke + /// + public string Name { get; set; } = string.Empty; + + /// + /// The JSON arguments for the tool call + /// + public IDictionary Input { get; set; } = new Dictionary(); + + + + #region Load Methods + + /// + /// Load a AnthropicToolUseBlock instance from a dictionary. + /// + /// The dictionary containing the data. + /// Optional context with pre/post processing callbacks. + /// The loaded AnthropicToolUseBlock instance. + public static AnthropicToolUseBlock Load(Dictionary data, LoadContext? context = null) + { + if (context is not null) + { + data = context.ProcessInput(data); + } + + + // Create new instance + var instance = new AnthropicToolUseBlock(); + + + if (data.TryGetValue("type", out var typeValue) && typeValue is not null) + { + instance.Type = typeValue?.ToString()!; + } + + if (data.TryGetValue("id", out var idValue) && idValue is not null) + { + instance.Id = idValue?.ToString()!; + } + + if (data.TryGetValue("name", out var nameValue) && nameValue is not null) + { + instance.Name = nameValue?.ToString()!; + } + + if (data.TryGetValue("input", out var inputValue) && inputValue is not null) + { + instance.Input = inputValue.GetDictionary()!; + } + + if (context is not null) + { + instance = context.ProcessOutput(instance); + } + return instance; + } + + + #endregion + + #region Save Methods + + /// + /// Save the AnthropicToolUseBlock instance to a dictionary. + /// + /// Optional context with pre/post processing callbacks. + /// The dictionary representation of this instance. + public Dictionary Save(SaveContext? context = null) + { + var obj = this; + if (context is not null) + { + obj = context.ProcessObject(obj); + } + + + var result = new Dictionary(); + + + result["type"] = obj.Type; + + + result["id"] = obj.Id; + + + result["name"] = obj.Name; + + + result["input"] = obj.Input; + + + if (context is not null) + { + result = context.ProcessDict(result); + } + + return result; + } + + + /// + /// Convert the AnthropicToolUseBlock instance to a YAML string. + /// + /// Optional context with pre/post processing callbacks. + /// The YAML string representation of this instance. + public string ToYaml(SaveContext? context = null) + { + context ??= new SaveContext(); + return context.ToYaml(Save(context)); + } + + /// + /// Convert the AnthropicToolUseBlock instance to a JSON string. + /// + /// Optional context with pre/post processing callbacks. + /// Whether to indent the output. Defaults to true. + /// The JSON string representation of this instance. + public string ToJson(SaveContext? context = null, bool indent = true) + { + context ??= new SaveContext(); + return context.ToJson(Save(context), indent); + } + + /// + /// Load a AnthropicToolUseBlock instance from a JSON string. + /// + /// The JSON string to parse. + /// Optional context with pre/post processing callbacks. + /// The loaded AnthropicToolUseBlock instance. + public static AnthropicToolUseBlock FromJson(string json, LoadContext? context = null) + { + using var doc = JsonDocument.Parse(json); + Dictionary dict; + dict = JsonSerializer.Deserialize>(json, JsonUtils.Options) + ?? throw new ArgumentException("Failed to parse JSON as dictionary"); + + return Load(dict, context); + } + + /// + /// Load a AnthropicToolUseBlock instance from a YAML string. + /// + /// The YAML string to parse. + /// Optional context with pre/post processing callbacks. + /// The loaded AnthropicToolUseBlock instance. + public static AnthropicToolUseBlock FromYaml(string yaml, LoadContext? context = null) + { + var dict = YamlUtils.Deserializer.Deserialize>(yaml) + ?? throw new ArgumentException("Failed to parse YAML as dictionary"); + + return Load(dict, context); + } + + #endregion +} diff --git a/runtime/csharp/Prompty.Core/Model/wire/AnthropicUsage.cs b/runtime/csharp/Prompty.Core/Model/wire/AnthropicUsage.cs new file mode 100644 index 00000000..6b5a92e0 --- /dev/null +++ b/runtime/csharp/Prompty.Core/Model/wire/AnthropicUsage.cs @@ -0,0 +1,168 @@ +// Copyright (c) Microsoft. All rights reserved. +using System.Text.Json; +using YamlDotNet.Serialization; + +#pragma warning disable IDE0130 +namespace Prompty.Core; +#pragma warning restore IDE0130 + +/// +/// Usage statistics returned in an Anthropic Messages API response. +/// +public partial class AnthropicUsage +{ + /// + /// The shorthand property name for this type, if any. + /// + public static string? ShorthandProperty => null; + + /// + /// Initializes a new instance of . + /// +#pragma warning disable CS8618 + public AnthropicUsage() + { + } +#pragma warning restore CS8618 + + /// + /// Number of input tokens consumed + /// + public int InputTokens { get; set; } + + /// + /// Number of output tokens generated + /// + public int OutputTokens { get; set; } + + + + #region Load Methods + + /// + /// Load a AnthropicUsage instance from a dictionary. + /// + /// The dictionary containing the data. + /// Optional context with pre/post processing callbacks. + /// The loaded AnthropicUsage instance. + public static AnthropicUsage Load(Dictionary data, LoadContext? context = null) + { + if (context is not null) + { + data = context.ProcessInput(data); + } + + + // Create new instance + var instance = new AnthropicUsage(); + + + if (data.TryGetValue("input_tokens", out var input_tokensValue) && input_tokensValue is not null) + { + instance.InputTokens = Convert.ToInt32(input_tokensValue); + } + + if (data.TryGetValue("output_tokens", out var output_tokensValue) && output_tokensValue is not null) + { + instance.OutputTokens = Convert.ToInt32(output_tokensValue); + } + + if (context is not null) + { + instance = context.ProcessOutput(instance); + } + return instance; + } + + + #endregion + + #region Save Methods + + /// + /// Save the AnthropicUsage instance to a dictionary. + /// + /// Optional context with pre/post processing callbacks. + /// The dictionary representation of this instance. + public Dictionary Save(SaveContext? context = null) + { + var obj = this; + if (context is not null) + { + obj = context.ProcessObject(obj); + } + + + var result = new Dictionary(); + + + result["input_tokens"] = obj.InputTokens; + + + result["output_tokens"] = obj.OutputTokens; + + + if (context is not null) + { + result = context.ProcessDict(result); + } + + return result; + } + + + /// + /// Convert the AnthropicUsage instance to a YAML string. + /// + /// Optional context with pre/post processing callbacks. + /// The YAML string representation of this instance. + public string ToYaml(SaveContext? context = null) + { + context ??= new SaveContext(); + return context.ToYaml(Save(context)); + } + + /// + /// Convert the AnthropicUsage instance to a JSON string. + /// + /// Optional context with pre/post processing callbacks. + /// Whether to indent the output. Defaults to true. + /// The JSON string representation of this instance. + public string ToJson(SaveContext? context = null, bool indent = true) + { + context ??= new SaveContext(); + return context.ToJson(Save(context), indent); + } + + /// + /// Load a AnthropicUsage instance from a JSON string. + /// + /// The JSON string to parse. + /// Optional context with pre/post processing callbacks. + /// The loaded AnthropicUsage instance. + public static AnthropicUsage FromJson(string json, LoadContext? context = null) + { + using var doc = JsonDocument.Parse(json); + Dictionary dict; + dict = JsonSerializer.Deserialize>(json, JsonUtils.Options) + ?? throw new ArgumentException("Failed to parse JSON as dictionary"); + + return Load(dict, context); + } + + /// + /// Load a AnthropicUsage instance from a YAML string. + /// + /// The YAML string to parse. + /// Optional context with pre/post processing callbacks. + /// The loaded AnthropicUsage instance. + public static AnthropicUsage FromYaml(string yaml, LoadContext? context = null) + { + var dict = YamlUtils.Deserializer.Deserialize>(yaml) + ?? throw new ArgumentException("Failed to parse YAML as dictionary"); + + return Load(dict, context); + } + + #endregion +} diff --git a/runtime/csharp/Prompty.Core/Model/wire/AnthropicWireMessage.cs b/runtime/csharp/Prompty.Core/Model/wire/AnthropicWireMessage.cs new file mode 100644 index 00000000..5d03e795 --- /dev/null +++ b/runtime/csharp/Prompty.Core/Model/wire/AnthropicWireMessage.cs @@ -0,0 +1,172 @@ +// Copyright (c) Microsoft. All rights reserved. +using System.Text.Json; +using YamlDotNet.Serialization; + +#pragma warning disable IDE0130 +namespace Prompty.Core; +#pragma warning restore IDE0130 + +/// +/// A single message in the Anthropic Messages API wire format. +/// +/// Anthropic always uses the array-of-blocks form for content, +/// +/// even when there is only one text block (§7.5). +/// +public partial class AnthropicWireMessage +{ + /// + /// The shorthand property name for this type, if any. + /// + public static string? ShorthandProperty => null; + + /// + /// Initializes a new instance of . + /// +#pragma warning disable CS8618 + public AnthropicWireMessage() + { + } +#pragma warning restore CS8618 + + /// + /// The message role ('user' or 'assistant') + /// + public string Role { get; set; } = string.Empty; + + /// + /// Array of typed content blocks (AnthropicTextBlock | AnthropicImageBlock | AnthropicToolUseBlock | AnthropicToolResultBlock) + /// + public IList Content { get; set; } = []; + + + + #region Load Methods + + /// + /// Load a AnthropicWireMessage instance from a dictionary. + /// + /// The dictionary containing the data. + /// Optional context with pre/post processing callbacks. + /// The loaded AnthropicWireMessage instance. + public static AnthropicWireMessage Load(Dictionary data, LoadContext? context = null) + { + if (context is not null) + { + data = context.ProcessInput(data); + } + + + // Create new instance + var instance = new AnthropicWireMessage(); + + + if (data.TryGetValue("role", out var roleValue) && roleValue is not null) + { + instance.Role = roleValue?.ToString()!; + } + + if (data.TryGetValue("content", out var contentValue) && contentValue is not null) + { + instance.Content = (contentValue as IEnumerable)?.ToList() ?? []; + } + + if (context is not null) + { + instance = context.ProcessOutput(instance); + } + return instance; + } + + + #endregion + + #region Save Methods + + /// + /// Save the AnthropicWireMessage instance to a dictionary. + /// + /// Optional context with pre/post processing callbacks. + /// The dictionary representation of this instance. + public Dictionary Save(SaveContext? context = null) + { + var obj = this; + if (context is not null) + { + obj = context.ProcessObject(obj); + } + + + var result = new Dictionary(); + + + result["role"] = obj.Role; + + + result["content"] = obj.Content; + + + if (context is not null) + { + result = context.ProcessDict(result); + } + + return result; + } + + + /// + /// Convert the AnthropicWireMessage instance to a YAML string. + /// + /// Optional context with pre/post processing callbacks. + /// The YAML string representation of this instance. + public string ToYaml(SaveContext? context = null) + { + context ??= new SaveContext(); + return context.ToYaml(Save(context)); + } + + /// + /// Convert the AnthropicWireMessage instance to a JSON string. + /// + /// Optional context with pre/post processing callbacks. + /// Whether to indent the output. Defaults to true. + /// The JSON string representation of this instance. + public string ToJson(SaveContext? context = null, bool indent = true) + { + context ??= new SaveContext(); + return context.ToJson(Save(context), indent); + } + + /// + /// Load a AnthropicWireMessage instance from a JSON string. + /// + /// The JSON string to parse. + /// Optional context with pre/post processing callbacks. + /// The loaded AnthropicWireMessage instance. + public static AnthropicWireMessage FromJson(string json, LoadContext? context = null) + { + using var doc = JsonDocument.Parse(json); + Dictionary dict; + dict = JsonSerializer.Deserialize>(json, JsonUtils.Options) + ?? throw new ArgumentException("Failed to parse JSON as dictionary"); + + return Load(dict, context); + } + + /// + /// Load a AnthropicWireMessage instance from a YAML string. + /// + /// The YAML string to parse. + /// Optional context with pre/post processing callbacks. + /// The loaded AnthropicWireMessage instance. + public static AnthropicWireMessage FromYaml(string yaml, LoadContext? context = null) + { + var dict = YamlUtils.Deserializer.Deserialize>(yaml) + ?? throw new ArgumentException("Failed to parse YAML as dictionary"); + + return Load(dict, context); + } + + #endregion +} diff --git a/runtime/go/prompty/model/anthropic_image_block.go b/runtime/go/prompty/model/anthropic_image_block.go new file mode 100644 index 00000000..64e4f4b4 --- /dev/null +++ b/runtime/go/prompty/model/anthropic_image_block.go @@ -0,0 +1,90 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. +// Group: wire + +package prompty + +import ( + "encoding/json" + + "gopkg.in/yaml.v3" +) + +// AnthropicImageBlock represents An image content block using base64-encoded data. +// Anthropic requires images as base64 with an explicit media type. + +type AnthropicImageBlock struct { + Type string `json:"type" yaml:"type"` + Source AnthropicImageSource `json:"source" yaml:"source"` +} + +// LoadAnthropicImageBlock creates a AnthropicImageBlock from a map[string]interface{} +func LoadAnthropicImageBlock(data interface{}, ctx *LoadContext) (AnthropicImageBlock, error) { + result := AnthropicImageBlock{} + + // Load from map + if m, ok := data.(map[string]interface{}); ok { + if val, ok := m["type"]; ok && val != nil { + result.Type = string(val.(string)) + } + if val, ok := m["source"]; ok && val != nil { + if m, ok := val.(map[string]interface{}); ok { + loaded, _ := LoadAnthropicImageSource(m, ctx) + result.Source = loaded + } + } + } + + return result, nil +} + +// Save serializes AnthropicImageBlock to map[string]interface{} +func (obj *AnthropicImageBlock) Save(ctx *SaveContext) map[string]interface{} { + result := make(map[string]interface{}) + result["type"] = obj.Type + + result["source"] = obj.Source.Save(ctx) + + return result +} + +// ToJSON serializes AnthropicImageBlock to JSON string +func (obj *AnthropicImageBlock) ToJSON() (string, error) { + ctx := NewSaveContext() + data := obj.Save(ctx) + bytes, err := json.Marshal(data) + if err != nil { + return "", err + } + return string(bytes), nil +} + +// ToYAML serializes AnthropicImageBlock to YAML string +func (obj *AnthropicImageBlock) ToYAML() (string, error) { + ctx := NewSaveContext() + data := obj.Save(ctx) + bytes, err := yaml.Marshal(data) + if err != nil { + return "", err + } + return string(bytes), nil +} + +// FromJSON creates AnthropicImageBlock from JSON string +func AnthropicImageBlockFromJSON(jsonStr string) (AnthropicImageBlock, error) { + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonStr), &data); err != nil { + return AnthropicImageBlock{}, err + } + ctx := NewLoadContext() + return LoadAnthropicImageBlock(data, ctx) +} + +// FromYAML creates AnthropicImageBlock from YAML string +func AnthropicImageBlockFromYAML(yamlStr string) (AnthropicImageBlock, error) { + var data map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlStr), &data); err != nil { + return AnthropicImageBlock{}, err + } + ctx := NewLoadContext() + return LoadAnthropicImageBlock(data, ctx) +} diff --git a/runtime/go/prompty/model/anthropic_image_block_test.go b/runtime/go/prompty/model/anthropic_image_block_test.go new file mode 100644 index 00000000..8e719bf5 --- /dev/null +++ b/runtime/go/prompty/model/anthropic_image_block_test.go @@ -0,0 +1,3 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +package prompty_test diff --git a/runtime/go/prompty/model/anthropic_image_source.go b/runtime/go/prompty/model/anthropic_image_source.go new file mode 100644 index 00000000..a653450e --- /dev/null +++ b/runtime/go/prompty/model/anthropic_image_source.go @@ -0,0 +1,90 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. +// Group: wire + +package prompty + +import ( + "encoding/json" + + "gopkg.in/yaml.v3" +) + +// AnthropicImageSource represents Source descriptor for an Anthropic base64 image. + +type AnthropicImageSource struct { + Type string `json:"type" yaml:"type"` + MediaType string `json:"media_type" yaml:"media_type"` + Data string `json:"data" yaml:"data"` +} + +// LoadAnthropicImageSource creates a AnthropicImageSource from a map[string]interface{} +func LoadAnthropicImageSource(data interface{}, ctx *LoadContext) (AnthropicImageSource, error) { + result := AnthropicImageSource{} + + // Load from map + if m, ok := data.(map[string]interface{}); ok { + if val, ok := m["type"]; ok && val != nil { + result.Type = string(val.(string)) + } + if val, ok := m["media_type"]; ok && val != nil { + result.MediaType = string(val.(string)) + } + if val, ok := m["data"]; ok && val != nil { + result.Data = string(val.(string)) + } + } + + return result, nil +} + +// Save serializes AnthropicImageSource to map[string]interface{} +func (obj *AnthropicImageSource) Save(ctx *SaveContext) map[string]interface{} { + result := make(map[string]interface{}) + result["type"] = obj.Type + result["media_type"] = obj.MediaType + result["data"] = obj.Data + + return result +} + +// ToJSON serializes AnthropicImageSource to JSON string +func (obj *AnthropicImageSource) ToJSON() (string, error) { + ctx := NewSaveContext() + data := obj.Save(ctx) + bytes, err := json.Marshal(data) + if err != nil { + return "", err + } + return string(bytes), nil +} + +// ToYAML serializes AnthropicImageSource to YAML string +func (obj *AnthropicImageSource) ToYAML() (string, error) { + ctx := NewSaveContext() + data := obj.Save(ctx) + bytes, err := yaml.Marshal(data) + if err != nil { + return "", err + } + return string(bytes), nil +} + +// FromJSON creates AnthropicImageSource from JSON string +func AnthropicImageSourceFromJSON(jsonStr string) (AnthropicImageSource, error) { + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonStr), &data); err != nil { + return AnthropicImageSource{}, err + } + ctx := NewLoadContext() + return LoadAnthropicImageSource(data, ctx) +} + +// FromYAML creates AnthropicImageSource from YAML string +func AnthropicImageSourceFromYAML(yamlStr string) (AnthropicImageSource, error) { + var data map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlStr), &data); err != nil { + return AnthropicImageSource{}, err + } + ctx := NewLoadContext() + return LoadAnthropicImageSource(data, ctx) +} diff --git a/runtime/go/prompty/model/anthropic_image_source_test.go b/runtime/go/prompty/model/anthropic_image_source_test.go new file mode 100644 index 00000000..55b40a5d --- /dev/null +++ b/runtime/go/prompty/model/anthropic_image_source_test.go @@ -0,0 +1,154 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +package prompty_test + +import ( + "encoding/json" + "testing" + + "gopkg.in/yaml.v3" + + "prompty/model" +) + +// TestAnthropicImageSourceLoadJSON tests loading AnthropicImageSource from JSON +func TestAnthropicImageSourceLoadJSON(t *testing.T) { + jsonData := ` +{ + "media_type": "image/png", + "data": "iVBORw0KGgo..." +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicImageSource(data, ctx) + if err != nil { + t.Fatalf("Failed to load AnthropicImageSource: %v", err) + } + if instance.MediaType != "image/png" { + t.Errorf(`Expected MediaType to be "image/png", got %v`, instance.MediaType) + } + if instance.Data != "iVBORw0KGgo..." { + t.Errorf(`Expected Data to be "iVBORw0KGgo...", got %v`, instance.Data) + } +} + +// TestAnthropicImageSourceLoadYAML tests loading AnthropicImageSource from YAML +func TestAnthropicImageSourceLoadYAML(t *testing.T) { + yamlData := ` +media_type: image/png +data: iVBORw0KGgo... + +` + var data map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlData), &data); err != nil { + t.Fatalf("Failed to parse YAML: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicImageSource(data, ctx) + if err != nil { + t.Fatalf("Failed to load AnthropicImageSource: %v", err) + } + if instance.MediaType != "image/png" { + t.Errorf(`Expected MediaType to be "image/png", got %v`, instance.MediaType) + } + if instance.Data != "iVBORw0KGgo..." { + t.Errorf(`Expected Data to be "iVBORw0KGgo...", got %v`, instance.Data) + } +} + +// TestAnthropicImageSourceRoundtrip tests load -> save -> load produces equivalent data +func TestAnthropicImageSourceRoundtrip(t *testing.T) { + jsonData := ` +{ + "media_type": "image/png", + "data": "iVBORw0KGgo..." +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + loadCtx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicImageSource(data, loadCtx) + if err != nil { + t.Fatalf("Failed to load AnthropicImageSource: %v", err) + } + saveCtx := prompty.NewSaveContext() + savedData := instance.Save(saveCtx) + + reloaded, err := prompty.LoadAnthropicImageSource(savedData, loadCtx) + if err != nil { + t.Fatalf("Failed to reload AnthropicImageSource: %v", err) + } + if reloaded.MediaType != "image/png" { + t.Errorf(`Expected MediaType to be "image/png", got %v`, reloaded.MediaType) + } + if reloaded.Data != "iVBORw0KGgo..." { + t.Errorf(`Expected Data to be "iVBORw0KGgo...", got %v`, reloaded.Data) + } +} + +// TestAnthropicImageSourceToJSON tests that ToJSON produces valid JSON +func TestAnthropicImageSourceToJSON(t *testing.T) { + jsonData := ` +{ + "media_type": "image/png", + "data": "iVBORw0KGgo..." +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicImageSource(data, ctx) + if err != nil { + t.Fatalf("Failed to load AnthropicImageSource: %v", err) + } + jsonOutput, err := instance.ToJSON() + if err != nil { + t.Fatalf("Failed to convert to JSON: %v", err) + } + + var parsed map[string]interface{} + if err := json.Unmarshal([]byte(jsonOutput), &parsed); err != nil { + t.Fatalf("Failed to parse generated JSON: %v", err) + } +} + +// TestAnthropicImageSourceToYAML tests that ToYAML produces valid YAML +func TestAnthropicImageSourceToYAML(t *testing.T) { + jsonData := ` +{ + "media_type": "image/png", + "data": "iVBORw0KGgo..." +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicImageSource(data, ctx) + if err != nil { + t.Fatalf("Failed to load AnthropicImageSource: %v", err) + } + yamlOutput, err := instance.ToYAML() + if err != nil { + t.Fatalf("Failed to convert to YAML: %v", err) + } + + var parsed map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlOutput), &parsed); err != nil { + t.Fatalf("Failed to parse generated YAML: %v", err) + } +} diff --git a/runtime/go/prompty/model/anthropic_messages_request.go b/runtime/go/prompty/model/anthropic_messages_request.go new file mode 100644 index 00000000..6c6f3c7e --- /dev/null +++ b/runtime/go/prompty/model/anthropic_messages_request.go @@ -0,0 +1,210 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. +// Group: wire + +package prompty + +import ( + "encoding/json" + + "gopkg.in/yaml.v3" +) + +// AnthropicMessagesRequest represents The full request body for the Anthropic Messages API (§7.5). + +type AnthropicMessagesRequest struct { + Model string `json:"model" yaml:"model"` + Messages []AnthropicWireMessage `json:"messages" yaml:"messages"` + MaxTokens int32 `json:"max_tokens" yaml:"max_tokens"` + System *string `json:"system,omitempty" yaml:"system,omitempty"` + Temperature *float32 `json:"temperature,omitempty" yaml:"temperature,omitempty"` + TopP *float32 `json:"top_p,omitempty" yaml:"top_p,omitempty"` + TopK *int32 `json:"top_k,omitempty" yaml:"top_k,omitempty"` + StopSequences []string `json:"stop_sequences,omitempty" yaml:"stop_sequences,omitempty"` + Tools []AnthropicToolDefinition `json:"tools,omitempty" yaml:"tools,omitempty"` +} + +// LoadAnthropicMessagesRequest creates a AnthropicMessagesRequest from a map[string]interface{} +func LoadAnthropicMessagesRequest(data interface{}, ctx *LoadContext) (AnthropicMessagesRequest, error) { + result := AnthropicMessagesRequest{} + + // Load from map + if m, ok := data.(map[string]interface{}); ok { + if val, ok := m["model"]; ok && val != nil { + result.Model = string(val.(string)) + } + if val, ok := m["messages"]; ok && val != nil { + if arr, ok := val.([]interface{}); ok { + result.Messages = make([]AnthropicWireMessage, len(arr)) + for i, v := range arr { + if item, ok := v.(map[string]interface{}); ok { + loaded, _ := LoadAnthropicWireMessage(item, ctx) + result.Messages[i] = loaded + } + } + } + } + if val, ok := m["max_tokens"]; ok && val != nil { // Handle various numeric types from JSON/YAML/roundtrip + var v int32 + switch n := val.(type) { + case int: + v = int32(n) + case int32: + v = int32(n) + case int64: + v = int32(n) + case float64: + v = int32(n) + } + result.MaxTokens = v + } + if val, ok := m["system"]; ok && val != nil { + v := string(val.(string)) + result.System = &v + } + if val, ok := m["temperature"]; ok && val != nil { // Handle various numeric types from JSON/YAML/roundtrip + var v float32 + switch n := val.(type) { + case int: + v = float32(n) + case int32: + v = float32(n) + case int64: + v = float32(n) + case float32: + v = n + case float64: + v = float32(n) + } + result.Temperature = &v + } + if val, ok := m["top_p"]; ok && val != nil { // Handle various numeric types from JSON/YAML/roundtrip + var v float32 + switch n := val.(type) { + case int: + v = float32(n) + case int32: + v = float32(n) + case int64: + v = float32(n) + case float32: + v = n + case float64: + v = float32(n) + } + result.TopP = &v + } + if val, ok := m["top_k"]; ok && val != nil { // Handle various numeric types from JSON/YAML/roundtrip + var v int32 + switch n := val.(type) { + case int: + v = int32(n) + case int32: + v = int32(n) + case int64: + v = int32(n) + case float64: + v = int32(n) + } + result.TopK = &v + } + if val, ok := m["stop_sequences"]; ok && val != nil { + if arr, ok := val.([]interface{}); ok { + result.StopSequences = make([]string, len(arr)) + for i, v := range arr { + result.StopSequences[i] = v.(string) + } + } + } + if val, ok := m["tools"]; ok && val != nil { + if arr, ok := val.([]interface{}); ok { + result.Tools = make([]AnthropicToolDefinition, len(arr)) + for i, v := range arr { + if item, ok := v.(map[string]interface{}); ok { + loaded, _ := LoadAnthropicToolDefinition(item, ctx) + result.Tools[i] = loaded + } + } + } + } + } + + return result, nil +} + +// Save serializes AnthropicMessagesRequest to map[string]interface{} +func (obj *AnthropicMessagesRequest) Save(ctx *SaveContext) map[string]interface{} { + result := make(map[string]interface{}) + result["model"] = obj.Model + if obj.Messages != nil { + arr := make([]interface{}, len(obj.Messages)) + for i, item := range obj.Messages { + arr[i] = item.Save(ctx) + } + result["messages"] = arr + } + result["max_tokens"] = obj.MaxTokens + if obj.System != nil { + result["system"] = *obj.System + } + if obj.Temperature != nil { + result["temperature"] = *obj.Temperature + } + if obj.TopP != nil { + result["top_p"] = *obj.TopP + } + if obj.TopK != nil { + result["top_k"] = *obj.TopK + } + result["stop_sequences"] = obj.StopSequences + if obj.Tools != nil { + arr := make([]interface{}, len(obj.Tools)) + for i, item := range obj.Tools { + arr[i] = item.Save(ctx) + } + result["tools"] = arr + } + + return result +} + +// ToJSON serializes AnthropicMessagesRequest to JSON string +func (obj *AnthropicMessagesRequest) ToJSON() (string, error) { + ctx := NewSaveContext() + data := obj.Save(ctx) + bytes, err := json.Marshal(data) + if err != nil { + return "", err + } + return string(bytes), nil +} + +// ToYAML serializes AnthropicMessagesRequest to YAML string +func (obj *AnthropicMessagesRequest) ToYAML() (string, error) { + ctx := NewSaveContext() + data := obj.Save(ctx) + bytes, err := yaml.Marshal(data) + if err != nil { + return "", err + } + return string(bytes), nil +} + +// FromJSON creates AnthropicMessagesRequest from JSON string +func AnthropicMessagesRequestFromJSON(jsonStr string) (AnthropicMessagesRequest, error) { + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonStr), &data); err != nil { + return AnthropicMessagesRequest{}, err + } + ctx := NewLoadContext() + return LoadAnthropicMessagesRequest(data, ctx) +} + +// FromYAML creates AnthropicMessagesRequest from YAML string +func AnthropicMessagesRequestFromYAML(yamlStr string) (AnthropicMessagesRequest, error) { + var data map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlStr), &data); err != nil { + return AnthropicMessagesRequest{}, err + } + ctx := NewLoadContext() + return LoadAnthropicMessagesRequest(data, ctx) +} diff --git a/runtime/go/prompty/model/anthropic_messages_request_test.go b/runtime/go/prompty/model/anthropic_messages_request_test.go new file mode 100644 index 00000000..38b6ccf9 --- /dev/null +++ b/runtime/go/prompty/model/anthropic_messages_request_test.go @@ -0,0 +1,224 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +package prompty_test + +import ( + "encoding/json" + "testing" + + "gopkg.in/yaml.v3" + + "prompty/model" +) + +// TestAnthropicMessagesRequestLoadJSON tests loading AnthropicMessagesRequest from JSON +func TestAnthropicMessagesRequestLoadJSON(t *testing.T) { + jsonData := ` +{ + "model": "claude-sonnet-4-20250514", + "max_tokens": 4096, + "system": "You are a helpful assistant.", + "temperature": 0.7, + "top_p": 0.9, + "top_k": 40, + "stop_sequences": [ + "\n\nHuman:" + ] +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicMessagesRequest(data, ctx) + if err != nil { + t.Fatalf("Failed to load AnthropicMessagesRequest: %v", err) + } + if instance.Model != "claude-sonnet-4-20250514" { + t.Errorf(`Expected Model to be "claude-sonnet-4-20250514", got %v`, instance.Model) + } + if instance.MaxTokens != 4096 { + t.Errorf(`Expected MaxTokens to be 4096, got %v`, instance.MaxTokens) + } + if instance.System == nil || *instance.System != "You are a helpful assistant." { + t.Errorf(`Expected System to be "You are a helpful assistant.", got %v`, instance.System) + } + if instance.Temperature == nil || *instance.Temperature != 0.7 { + t.Errorf(`Expected Temperature to be 0.7, got %v`, instance.Temperature) + } + if instance.TopP == nil || *instance.TopP != 0.9 { + t.Errorf(`Expected TopP to be 0.9, got %v`, instance.TopP) + } + if instance.TopK == nil || *instance.TopK != 40 { + t.Errorf(`Expected TopK to be 40, got %v`, instance.TopK) + } +} + +// TestAnthropicMessagesRequestLoadYAML tests loading AnthropicMessagesRequest from YAML +func TestAnthropicMessagesRequestLoadYAML(t *testing.T) { + yamlData := ` +model: claude-sonnet-4-20250514 +max_tokens: 4096 +system: You are a helpful assistant. +temperature: 0.7 +top_p: 0.9 +top_k: 40 +stop_sequences: + - "\n\nHuman:" + +` + var data map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlData), &data); err != nil { + t.Fatalf("Failed to parse YAML: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicMessagesRequest(data, ctx) + if err != nil { + t.Fatalf("Failed to load AnthropicMessagesRequest: %v", err) + } + if instance.Model != "claude-sonnet-4-20250514" { + t.Errorf(`Expected Model to be "claude-sonnet-4-20250514", got %v`, instance.Model) + } + if instance.MaxTokens != 4096 { + t.Errorf(`Expected MaxTokens to be 4096, got %v`, instance.MaxTokens) + } + if instance.System == nil || *instance.System != "You are a helpful assistant." { + t.Errorf(`Expected System to be "You are a helpful assistant.", got %v`, instance.System) + } + if instance.Temperature == nil || *instance.Temperature != 0.7 { + t.Errorf(`Expected Temperature to be 0.7, got %v`, instance.Temperature) + } + if instance.TopP == nil || *instance.TopP != 0.9 { + t.Errorf(`Expected TopP to be 0.9, got %v`, instance.TopP) + } + if instance.TopK == nil || *instance.TopK != 40 { + t.Errorf(`Expected TopK to be 40, got %v`, instance.TopK) + } +} + +// TestAnthropicMessagesRequestRoundtrip tests load -> save -> load produces equivalent data +func TestAnthropicMessagesRequestRoundtrip(t *testing.T) { + jsonData := ` +{ + "model": "claude-sonnet-4-20250514", + "max_tokens": 4096, + "system": "You are a helpful assistant.", + "temperature": 0.7, + "top_p": 0.9, + "top_k": 40, + "stop_sequences": [ + "\n\nHuman:" + ] +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + loadCtx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicMessagesRequest(data, loadCtx) + if err != nil { + t.Fatalf("Failed to load AnthropicMessagesRequest: %v", err) + } + saveCtx := prompty.NewSaveContext() + savedData := instance.Save(saveCtx) + + reloaded, err := prompty.LoadAnthropicMessagesRequest(savedData, loadCtx) + if err != nil { + t.Fatalf("Failed to reload AnthropicMessagesRequest: %v", err) + } + if reloaded.Model != "claude-sonnet-4-20250514" { + t.Errorf(`Expected Model to be "claude-sonnet-4-20250514", got %v`, reloaded.Model) + } + if reloaded.MaxTokens != 4096 { + t.Errorf(`Expected MaxTokens to be 4096, got %v`, reloaded.MaxTokens) + } + if reloaded.System == nil || *reloaded.System != "You are a helpful assistant." { + t.Errorf(`Expected System to be "You are a helpful assistant.", got %v`, reloaded.System) + } + if reloaded.Temperature == nil || *reloaded.Temperature != 0.7 { + t.Errorf(`Expected Temperature to be 0.7, got %v`, reloaded.Temperature) + } + if reloaded.TopP == nil || *reloaded.TopP != 0.9 { + t.Errorf(`Expected TopP to be 0.9, got %v`, reloaded.TopP) + } + if reloaded.TopK == nil || *reloaded.TopK != 40 { + t.Errorf(`Expected TopK to be 40, got %v`, reloaded.TopK) + } +} + +// TestAnthropicMessagesRequestToJSON tests that ToJSON produces valid JSON +func TestAnthropicMessagesRequestToJSON(t *testing.T) { + jsonData := ` +{ + "model": "claude-sonnet-4-20250514", + "max_tokens": 4096, + "system": "You are a helpful assistant.", + "temperature": 0.7, + "top_p": 0.9, + "top_k": 40, + "stop_sequences": [ + "\n\nHuman:" + ] +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicMessagesRequest(data, ctx) + if err != nil { + t.Fatalf("Failed to load AnthropicMessagesRequest: %v", err) + } + jsonOutput, err := instance.ToJSON() + if err != nil { + t.Fatalf("Failed to convert to JSON: %v", err) + } + + var parsed map[string]interface{} + if err := json.Unmarshal([]byte(jsonOutput), &parsed); err != nil { + t.Fatalf("Failed to parse generated JSON: %v", err) + } +} + +// TestAnthropicMessagesRequestToYAML tests that ToYAML produces valid YAML +func TestAnthropicMessagesRequestToYAML(t *testing.T) { + jsonData := ` +{ + "model": "claude-sonnet-4-20250514", + "max_tokens": 4096, + "system": "You are a helpful assistant.", + "temperature": 0.7, + "top_p": 0.9, + "top_k": 40, + "stop_sequences": [ + "\n\nHuman:" + ] +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicMessagesRequest(data, ctx) + if err != nil { + t.Fatalf("Failed to load AnthropicMessagesRequest: %v", err) + } + yamlOutput, err := instance.ToYAML() + if err != nil { + t.Fatalf("Failed to convert to YAML: %v", err) + } + + var parsed map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlOutput), &parsed); err != nil { + t.Fatalf("Failed to parse generated YAML: %v", err) + } +} diff --git a/runtime/go/prompty/model/anthropic_messages_response.go b/runtime/go/prompty/model/anthropic_messages_response.go new file mode 100644 index 00000000..7b05b2f4 --- /dev/null +++ b/runtime/go/prompty/model/anthropic_messages_response.go @@ -0,0 +1,116 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. +// Group: wire + +package prompty + +import ( + "encoding/json" + + "gopkg.in/yaml.v3" +) + +// AnthropicMessagesResponse represents The response body from the Anthropic Messages API. + +type AnthropicMessagesResponse struct { + Id string `json:"id" yaml:"id"` + Type string `json:"type" yaml:"type"` + Role string `json:"role" yaml:"role"` + Content []interface{} `json:"content" yaml:"content"` + Model string `json:"model" yaml:"model"` + StopReason string `json:"stop_reason" yaml:"stop_reason"` + Usage AnthropicUsage `json:"usage" yaml:"usage"` +} + +// LoadAnthropicMessagesResponse creates a AnthropicMessagesResponse from a map[string]interface{} +func LoadAnthropicMessagesResponse(data interface{}, ctx *LoadContext) (AnthropicMessagesResponse, error) { + result := AnthropicMessagesResponse{} + + // Load from map + if m, ok := data.(map[string]interface{}); ok { + if val, ok := m["id"]; ok && val != nil { + result.Id = string(val.(string)) + } + if val, ok := m["type"]; ok && val != nil { + result.Type = string(val.(string)) + } + if val, ok := m["role"]; ok && val != nil { + result.Role = string(val.(string)) + } + if val, ok := m["content"]; ok && val != nil { + if arr, ok := val.([]interface{}); ok { + result.Content = arr + } + } + if val, ok := m["model"]; ok && val != nil { + result.Model = string(val.(string)) + } + if val, ok := m["stop_reason"]; ok && val != nil { + result.StopReason = string(val.(string)) + } + if val, ok := m["usage"]; ok && val != nil { + if m, ok := val.(map[string]interface{}); ok { + loaded, _ := LoadAnthropicUsage(m, ctx) + result.Usage = loaded + } + } + } + + return result, nil +} + +// Save serializes AnthropicMessagesResponse to map[string]interface{} +func (obj *AnthropicMessagesResponse) Save(ctx *SaveContext) map[string]interface{} { + result := make(map[string]interface{}) + result["id"] = obj.Id + result["type"] = obj.Type + result["role"] = obj.Role + result["content"] = obj.Content + result["model"] = obj.Model + result["stop_reason"] = obj.StopReason + + result["usage"] = obj.Usage.Save(ctx) + + return result +} + +// ToJSON serializes AnthropicMessagesResponse to JSON string +func (obj *AnthropicMessagesResponse) ToJSON() (string, error) { + ctx := NewSaveContext() + data := obj.Save(ctx) + bytes, err := json.Marshal(data) + if err != nil { + return "", err + } + return string(bytes), nil +} + +// ToYAML serializes AnthropicMessagesResponse to YAML string +func (obj *AnthropicMessagesResponse) ToYAML() (string, error) { + ctx := NewSaveContext() + data := obj.Save(ctx) + bytes, err := yaml.Marshal(data) + if err != nil { + return "", err + } + return string(bytes), nil +} + +// FromJSON creates AnthropicMessagesResponse from JSON string +func AnthropicMessagesResponseFromJSON(jsonStr string) (AnthropicMessagesResponse, error) { + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonStr), &data); err != nil { + return AnthropicMessagesResponse{}, err + } + ctx := NewLoadContext() + return LoadAnthropicMessagesResponse(data, ctx) +} + +// FromYAML creates AnthropicMessagesResponse from YAML string +func AnthropicMessagesResponseFromYAML(yamlStr string) (AnthropicMessagesResponse, error) { + var data map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlStr), &data); err != nil { + return AnthropicMessagesResponse{}, err + } + ctx := NewLoadContext() + return LoadAnthropicMessagesResponse(data, ctx) +} diff --git a/runtime/go/prompty/model/anthropic_messages_response_test.go b/runtime/go/prompty/model/anthropic_messages_response_test.go new file mode 100644 index 00000000..26f60fd7 --- /dev/null +++ b/runtime/go/prompty/model/anthropic_messages_response_test.go @@ -0,0 +1,168 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +package prompty_test + +import ( + "encoding/json" + "testing" + + "gopkg.in/yaml.v3" + + "prompty/model" +) + +// TestAnthropicMessagesResponseLoadJSON tests loading AnthropicMessagesResponse from JSON +func TestAnthropicMessagesResponseLoadJSON(t *testing.T) { + jsonData := ` +{ + "id": "msg_01XFDUDYJgAACzvnptvVoYEL", + "model": "claude-sonnet-4-20250514", + "stop_reason": "end_turn" +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicMessagesResponse(data, ctx) + if err != nil { + t.Fatalf("Failed to load AnthropicMessagesResponse: %v", err) + } + if instance.Id != "msg_01XFDUDYJgAACzvnptvVoYEL" { + t.Errorf(`Expected Id to be "msg_01XFDUDYJgAACzvnptvVoYEL", got %v`, instance.Id) + } + if instance.Model != "claude-sonnet-4-20250514" { + t.Errorf(`Expected Model to be "claude-sonnet-4-20250514", got %v`, instance.Model) + } + if instance.StopReason != "end_turn" { + t.Errorf(`Expected StopReason to be "end_turn", got %v`, instance.StopReason) + } +} + +// TestAnthropicMessagesResponseLoadYAML tests loading AnthropicMessagesResponse from YAML +func TestAnthropicMessagesResponseLoadYAML(t *testing.T) { + yamlData := ` +id: msg_01XFDUDYJgAACzvnptvVoYEL +model: claude-sonnet-4-20250514 +stop_reason: end_turn + +` + var data map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlData), &data); err != nil { + t.Fatalf("Failed to parse YAML: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicMessagesResponse(data, ctx) + if err != nil { + t.Fatalf("Failed to load AnthropicMessagesResponse: %v", err) + } + if instance.Id != "msg_01XFDUDYJgAACzvnptvVoYEL" { + t.Errorf(`Expected Id to be "msg_01XFDUDYJgAACzvnptvVoYEL", got %v`, instance.Id) + } + if instance.Model != "claude-sonnet-4-20250514" { + t.Errorf(`Expected Model to be "claude-sonnet-4-20250514", got %v`, instance.Model) + } + if instance.StopReason != "end_turn" { + t.Errorf(`Expected StopReason to be "end_turn", got %v`, instance.StopReason) + } +} + +// TestAnthropicMessagesResponseRoundtrip tests load -> save -> load produces equivalent data +func TestAnthropicMessagesResponseRoundtrip(t *testing.T) { + jsonData := ` +{ + "id": "msg_01XFDUDYJgAACzvnptvVoYEL", + "model": "claude-sonnet-4-20250514", + "stop_reason": "end_turn" +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + loadCtx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicMessagesResponse(data, loadCtx) + if err != nil { + t.Fatalf("Failed to load AnthropicMessagesResponse: %v", err) + } + saveCtx := prompty.NewSaveContext() + savedData := instance.Save(saveCtx) + + reloaded, err := prompty.LoadAnthropicMessagesResponse(savedData, loadCtx) + if err != nil { + t.Fatalf("Failed to reload AnthropicMessagesResponse: %v", err) + } + if reloaded.Id != "msg_01XFDUDYJgAACzvnptvVoYEL" { + t.Errorf(`Expected Id to be "msg_01XFDUDYJgAACzvnptvVoYEL", got %v`, reloaded.Id) + } + if reloaded.Model != "claude-sonnet-4-20250514" { + t.Errorf(`Expected Model to be "claude-sonnet-4-20250514", got %v`, reloaded.Model) + } + if reloaded.StopReason != "end_turn" { + t.Errorf(`Expected StopReason to be "end_turn", got %v`, reloaded.StopReason) + } +} + +// TestAnthropicMessagesResponseToJSON tests that ToJSON produces valid JSON +func TestAnthropicMessagesResponseToJSON(t *testing.T) { + jsonData := ` +{ + "id": "msg_01XFDUDYJgAACzvnptvVoYEL", + "model": "claude-sonnet-4-20250514", + "stop_reason": "end_turn" +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicMessagesResponse(data, ctx) + if err != nil { + t.Fatalf("Failed to load AnthropicMessagesResponse: %v", err) + } + jsonOutput, err := instance.ToJSON() + if err != nil { + t.Fatalf("Failed to convert to JSON: %v", err) + } + + var parsed map[string]interface{} + if err := json.Unmarshal([]byte(jsonOutput), &parsed); err != nil { + t.Fatalf("Failed to parse generated JSON: %v", err) + } +} + +// TestAnthropicMessagesResponseToYAML tests that ToYAML produces valid YAML +func TestAnthropicMessagesResponseToYAML(t *testing.T) { + jsonData := ` +{ + "id": "msg_01XFDUDYJgAACzvnptvVoYEL", + "model": "claude-sonnet-4-20250514", + "stop_reason": "end_turn" +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicMessagesResponse(data, ctx) + if err != nil { + t.Fatalf("Failed to load AnthropicMessagesResponse: %v", err) + } + yamlOutput, err := instance.ToYAML() + if err != nil { + t.Fatalf("Failed to convert to YAML: %v", err) + } + + var parsed map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlOutput), &parsed); err != nil { + t.Fatalf("Failed to parse generated YAML: %v", err) + } +} diff --git a/runtime/go/prompty/model/anthropic_text_block.go b/runtime/go/prompty/model/anthropic_text_block.go new file mode 100644 index 00000000..bf3bb3f0 --- /dev/null +++ b/runtime/go/prompty/model/anthropic_text_block.go @@ -0,0 +1,85 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. +// Group: wire + +package prompty + +import ( + "encoding/json" + + "gopkg.in/yaml.v3" +) + +// AnthropicTextBlock represents A text content block in Anthropic's array-of-blocks message format. + +type AnthropicTextBlock struct { + Type string `json:"type" yaml:"type"` + Text string `json:"text" yaml:"text"` +} + +// LoadAnthropicTextBlock creates a AnthropicTextBlock from a map[string]interface{} +func LoadAnthropicTextBlock(data interface{}, ctx *LoadContext) (AnthropicTextBlock, error) { + result := AnthropicTextBlock{} + + // Load from map + if m, ok := data.(map[string]interface{}); ok { + if val, ok := m["type"]; ok && val != nil { + result.Type = string(val.(string)) + } + if val, ok := m["text"]; ok && val != nil { + result.Text = string(val.(string)) + } + } + + return result, nil +} + +// Save serializes AnthropicTextBlock to map[string]interface{} +func (obj *AnthropicTextBlock) Save(ctx *SaveContext) map[string]interface{} { + result := make(map[string]interface{}) + result["type"] = obj.Type + result["text"] = obj.Text + + return result +} + +// ToJSON serializes AnthropicTextBlock to JSON string +func (obj *AnthropicTextBlock) ToJSON() (string, error) { + ctx := NewSaveContext() + data := obj.Save(ctx) + bytes, err := json.Marshal(data) + if err != nil { + return "", err + } + return string(bytes), nil +} + +// ToYAML serializes AnthropicTextBlock to YAML string +func (obj *AnthropicTextBlock) ToYAML() (string, error) { + ctx := NewSaveContext() + data := obj.Save(ctx) + bytes, err := yaml.Marshal(data) + if err != nil { + return "", err + } + return string(bytes), nil +} + +// FromJSON creates AnthropicTextBlock from JSON string +func AnthropicTextBlockFromJSON(jsonStr string) (AnthropicTextBlock, error) { + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonStr), &data); err != nil { + return AnthropicTextBlock{}, err + } + ctx := NewLoadContext() + return LoadAnthropicTextBlock(data, ctx) +} + +// FromYAML creates AnthropicTextBlock from YAML string +func AnthropicTextBlockFromYAML(yamlStr string) (AnthropicTextBlock, error) { + var data map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlStr), &data); err != nil { + return AnthropicTextBlock{}, err + } + ctx := NewLoadContext() + return LoadAnthropicTextBlock(data, ctx) +} diff --git a/runtime/go/prompty/model/anthropic_text_block_test.go b/runtime/go/prompty/model/anthropic_text_block_test.go new file mode 100644 index 00000000..9a70d241 --- /dev/null +++ b/runtime/go/prompty/model/anthropic_text_block_test.go @@ -0,0 +1,140 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +package prompty_test + +import ( + "encoding/json" + "testing" + + "gopkg.in/yaml.v3" + + "prompty/model" +) + +// TestAnthropicTextBlockLoadJSON tests loading AnthropicTextBlock from JSON +func TestAnthropicTextBlockLoadJSON(t *testing.T) { + jsonData := ` +{ + "text": "Hello, how can I help?" +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicTextBlock(data, ctx) + if err != nil { + t.Fatalf("Failed to load AnthropicTextBlock: %v", err) + } + if instance.Text != "Hello, how can I help?" { + t.Errorf(`Expected Text to be "Hello, how can I help?", got %v`, instance.Text) + } +} + +// TestAnthropicTextBlockLoadYAML tests loading AnthropicTextBlock from YAML +func TestAnthropicTextBlockLoadYAML(t *testing.T) { + yamlData := ` +text: Hello, how can I help? + +` + var data map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlData), &data); err != nil { + t.Fatalf("Failed to parse YAML: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicTextBlock(data, ctx) + if err != nil { + t.Fatalf("Failed to load AnthropicTextBlock: %v", err) + } + if instance.Text != "Hello, how can I help?" { + t.Errorf(`Expected Text to be "Hello, how can I help?", got %v`, instance.Text) + } +} + +// TestAnthropicTextBlockRoundtrip tests load -> save -> load produces equivalent data +func TestAnthropicTextBlockRoundtrip(t *testing.T) { + jsonData := ` +{ + "text": "Hello, how can I help?" +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + loadCtx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicTextBlock(data, loadCtx) + if err != nil { + t.Fatalf("Failed to load AnthropicTextBlock: %v", err) + } + saveCtx := prompty.NewSaveContext() + savedData := instance.Save(saveCtx) + + reloaded, err := prompty.LoadAnthropicTextBlock(savedData, loadCtx) + if err != nil { + t.Fatalf("Failed to reload AnthropicTextBlock: %v", err) + } + if reloaded.Text != "Hello, how can I help?" { + t.Errorf(`Expected Text to be "Hello, how can I help?", got %v`, reloaded.Text) + } +} + +// TestAnthropicTextBlockToJSON tests that ToJSON produces valid JSON +func TestAnthropicTextBlockToJSON(t *testing.T) { + jsonData := ` +{ + "text": "Hello, how can I help?" +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicTextBlock(data, ctx) + if err != nil { + t.Fatalf("Failed to load AnthropicTextBlock: %v", err) + } + jsonOutput, err := instance.ToJSON() + if err != nil { + t.Fatalf("Failed to convert to JSON: %v", err) + } + + var parsed map[string]interface{} + if err := json.Unmarshal([]byte(jsonOutput), &parsed); err != nil { + t.Fatalf("Failed to parse generated JSON: %v", err) + } +} + +// TestAnthropicTextBlockToYAML tests that ToYAML produces valid YAML +func TestAnthropicTextBlockToYAML(t *testing.T) { + jsonData := ` +{ + "text": "Hello, how can I help?" +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicTextBlock(data, ctx) + if err != nil { + t.Fatalf("Failed to load AnthropicTextBlock: %v", err) + } + yamlOutput, err := instance.ToYAML() + if err != nil { + t.Fatalf("Failed to convert to YAML: %v", err) + } + + var parsed map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlOutput), &parsed); err != nil { + t.Fatalf("Failed to parse generated YAML: %v", err) + } +} diff --git a/runtime/go/prompty/model/anthropic_tool_definition.go b/runtime/go/prompty/model/anthropic_tool_definition.go new file mode 100644 index 00000000..ffed9840 --- /dev/null +++ b/runtime/go/prompty/model/anthropic_tool_definition.go @@ -0,0 +1,97 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. +// Group: wire + +package prompty + +import ( + "encoding/json" + + "gopkg.in/yaml.v3" +) + +// AnthropicToolDefinition represents A tool definition in Anthropic's format. Unlike OpenAI which wraps +// tools in `{type: "function", function: {...}}`, Anthropic uses a +// flat structure with `input_schema` (§7.5). + +type AnthropicToolDefinition struct { + Name string `json:"name" yaml:"name"` + Description *string `json:"description,omitempty" yaml:"description,omitempty"` + InputSchema map[string]interface{} `json:"input_schema" yaml:"input_schema"` +} + +// LoadAnthropicToolDefinition creates a AnthropicToolDefinition from a map[string]interface{} +func LoadAnthropicToolDefinition(data interface{}, ctx *LoadContext) (AnthropicToolDefinition, error) { + result := AnthropicToolDefinition{} + + // Load from map + if m, ok := data.(map[string]interface{}); ok { + if val, ok := m["name"]; ok && val != nil { + result.Name = string(val.(string)) + } + if val, ok := m["description"]; ok && val != nil { + v := string(val.(string)) + result.Description = &v + } + if val, ok := m["input_schema"]; ok && val != nil { + if m, ok := val.(map[string]interface{}); ok { + result.InputSchema = m + } + } + } + + return result, nil +} + +// Save serializes AnthropicToolDefinition to map[string]interface{} +func (obj *AnthropicToolDefinition) Save(ctx *SaveContext) map[string]interface{} { + result := make(map[string]interface{}) + result["name"] = obj.Name + if obj.Description != nil { + result["description"] = *obj.Description + } + result["input_schema"] = obj.InputSchema + + return result +} + +// ToJSON serializes AnthropicToolDefinition to JSON string +func (obj *AnthropicToolDefinition) ToJSON() (string, error) { + ctx := NewSaveContext() + data := obj.Save(ctx) + bytes, err := json.Marshal(data) + if err != nil { + return "", err + } + return string(bytes), nil +} + +// ToYAML serializes AnthropicToolDefinition to YAML string +func (obj *AnthropicToolDefinition) ToYAML() (string, error) { + ctx := NewSaveContext() + data := obj.Save(ctx) + bytes, err := yaml.Marshal(data) + if err != nil { + return "", err + } + return string(bytes), nil +} + +// FromJSON creates AnthropicToolDefinition from JSON string +func AnthropicToolDefinitionFromJSON(jsonStr string) (AnthropicToolDefinition, error) { + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonStr), &data); err != nil { + return AnthropicToolDefinition{}, err + } + ctx := NewLoadContext() + return LoadAnthropicToolDefinition(data, ctx) +} + +// FromYAML creates AnthropicToolDefinition from YAML string +func AnthropicToolDefinitionFromYAML(yamlStr string) (AnthropicToolDefinition, error) { + var data map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlStr), &data); err != nil { + return AnthropicToolDefinition{}, err + } + ctx := NewLoadContext() + return LoadAnthropicToolDefinition(data, ctx) +} diff --git a/runtime/go/prompty/model/anthropic_tool_definition_test.go b/runtime/go/prompty/model/anthropic_tool_definition_test.go new file mode 100644 index 00000000..315d319a --- /dev/null +++ b/runtime/go/prompty/model/anthropic_tool_definition_test.go @@ -0,0 +1,154 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +package prompty_test + +import ( + "encoding/json" + "testing" + + "gopkg.in/yaml.v3" + + "prompty/model" +) + +// TestAnthropicToolDefinitionLoadJSON tests loading AnthropicToolDefinition from JSON +func TestAnthropicToolDefinitionLoadJSON(t *testing.T) { + jsonData := ` +{ + "name": "get_weather", + "description": "Get the current weather for a city" +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicToolDefinition(data, ctx) + if err != nil { + t.Fatalf("Failed to load AnthropicToolDefinition: %v", err) + } + if instance.Name != "get_weather" { + t.Errorf(`Expected Name to be "get_weather", got %v`, instance.Name) + } + if instance.Description == nil || *instance.Description != "Get the current weather for a city" { + t.Errorf(`Expected Description to be "Get the current weather for a city", got %v`, instance.Description) + } +} + +// TestAnthropicToolDefinitionLoadYAML tests loading AnthropicToolDefinition from YAML +func TestAnthropicToolDefinitionLoadYAML(t *testing.T) { + yamlData := ` +name: get_weather +description: Get the current weather for a city + +` + var data map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlData), &data); err != nil { + t.Fatalf("Failed to parse YAML: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicToolDefinition(data, ctx) + if err != nil { + t.Fatalf("Failed to load AnthropicToolDefinition: %v", err) + } + if instance.Name != "get_weather" { + t.Errorf(`Expected Name to be "get_weather", got %v`, instance.Name) + } + if instance.Description == nil || *instance.Description != "Get the current weather for a city" { + t.Errorf(`Expected Description to be "Get the current weather for a city", got %v`, instance.Description) + } +} + +// TestAnthropicToolDefinitionRoundtrip tests load -> save -> load produces equivalent data +func TestAnthropicToolDefinitionRoundtrip(t *testing.T) { + jsonData := ` +{ + "name": "get_weather", + "description": "Get the current weather for a city" +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + loadCtx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicToolDefinition(data, loadCtx) + if err != nil { + t.Fatalf("Failed to load AnthropicToolDefinition: %v", err) + } + saveCtx := prompty.NewSaveContext() + savedData := instance.Save(saveCtx) + + reloaded, err := prompty.LoadAnthropicToolDefinition(savedData, loadCtx) + if err != nil { + t.Fatalf("Failed to reload AnthropicToolDefinition: %v", err) + } + if reloaded.Name != "get_weather" { + t.Errorf(`Expected Name to be "get_weather", got %v`, reloaded.Name) + } + if reloaded.Description == nil || *reloaded.Description != "Get the current weather for a city" { + t.Errorf(`Expected Description to be "Get the current weather for a city", got %v`, reloaded.Description) + } +} + +// TestAnthropicToolDefinitionToJSON tests that ToJSON produces valid JSON +func TestAnthropicToolDefinitionToJSON(t *testing.T) { + jsonData := ` +{ + "name": "get_weather", + "description": "Get the current weather for a city" +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicToolDefinition(data, ctx) + if err != nil { + t.Fatalf("Failed to load AnthropicToolDefinition: %v", err) + } + jsonOutput, err := instance.ToJSON() + if err != nil { + t.Fatalf("Failed to convert to JSON: %v", err) + } + + var parsed map[string]interface{} + if err := json.Unmarshal([]byte(jsonOutput), &parsed); err != nil { + t.Fatalf("Failed to parse generated JSON: %v", err) + } +} + +// TestAnthropicToolDefinitionToYAML tests that ToYAML produces valid YAML +func TestAnthropicToolDefinitionToYAML(t *testing.T) { + jsonData := ` +{ + "name": "get_weather", + "description": "Get the current weather for a city" +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicToolDefinition(data, ctx) + if err != nil { + t.Fatalf("Failed to load AnthropicToolDefinition: %v", err) + } + yamlOutput, err := instance.ToYAML() + if err != nil { + t.Fatalf("Failed to convert to YAML: %v", err) + } + + var parsed map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlOutput), &parsed); err != nil { + t.Fatalf("Failed to parse generated YAML: %v", err) + } +} diff --git a/runtime/go/prompty/model/anthropic_tool_result_block.go b/runtime/go/prompty/model/anthropic_tool_result_block.go new file mode 100644 index 00000000..10114c63 --- /dev/null +++ b/runtime/go/prompty/model/anthropic_tool_result_block.go @@ -0,0 +1,90 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. +// Group: wire + +package prompty + +import ( + "encoding/json" + + "gopkg.in/yaml.v3" +) + +// AnthropicToolResultBlock represents A tool result content block sent back to the API with the tool's output. + +type AnthropicToolResultBlock struct { + Type string `json:"type" yaml:"type"` + ToolUseId string `json:"tool_use_id" yaml:"tool_use_id"` + Content string `json:"content" yaml:"content"` +} + +// LoadAnthropicToolResultBlock creates a AnthropicToolResultBlock from a map[string]interface{} +func LoadAnthropicToolResultBlock(data interface{}, ctx *LoadContext) (AnthropicToolResultBlock, error) { + result := AnthropicToolResultBlock{} + + // Load from map + if m, ok := data.(map[string]interface{}); ok { + if val, ok := m["type"]; ok && val != nil { + result.Type = string(val.(string)) + } + if val, ok := m["tool_use_id"]; ok && val != nil { + result.ToolUseId = string(val.(string)) + } + if val, ok := m["content"]; ok && val != nil { + result.Content = string(val.(string)) + } + } + + return result, nil +} + +// Save serializes AnthropicToolResultBlock to map[string]interface{} +func (obj *AnthropicToolResultBlock) Save(ctx *SaveContext) map[string]interface{} { + result := make(map[string]interface{}) + result["type"] = obj.Type + result["tool_use_id"] = obj.ToolUseId + result["content"] = obj.Content + + return result +} + +// ToJSON serializes AnthropicToolResultBlock to JSON string +func (obj *AnthropicToolResultBlock) ToJSON() (string, error) { + ctx := NewSaveContext() + data := obj.Save(ctx) + bytes, err := json.Marshal(data) + if err != nil { + return "", err + } + return string(bytes), nil +} + +// ToYAML serializes AnthropicToolResultBlock to YAML string +func (obj *AnthropicToolResultBlock) ToYAML() (string, error) { + ctx := NewSaveContext() + data := obj.Save(ctx) + bytes, err := yaml.Marshal(data) + if err != nil { + return "", err + } + return string(bytes), nil +} + +// FromJSON creates AnthropicToolResultBlock from JSON string +func AnthropicToolResultBlockFromJSON(jsonStr string) (AnthropicToolResultBlock, error) { + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonStr), &data); err != nil { + return AnthropicToolResultBlock{}, err + } + ctx := NewLoadContext() + return LoadAnthropicToolResultBlock(data, ctx) +} + +// FromYAML creates AnthropicToolResultBlock from YAML string +func AnthropicToolResultBlockFromYAML(yamlStr string) (AnthropicToolResultBlock, error) { + var data map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlStr), &data); err != nil { + return AnthropicToolResultBlock{}, err + } + ctx := NewLoadContext() + return LoadAnthropicToolResultBlock(data, ctx) +} diff --git a/runtime/go/prompty/model/anthropic_tool_result_block_test.go b/runtime/go/prompty/model/anthropic_tool_result_block_test.go new file mode 100644 index 00000000..145e2035 --- /dev/null +++ b/runtime/go/prompty/model/anthropic_tool_result_block_test.go @@ -0,0 +1,154 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +package prompty_test + +import ( + "encoding/json" + "testing" + + "gopkg.in/yaml.v3" + + "prompty/model" +) + +// TestAnthropicToolResultBlockLoadJSON tests loading AnthropicToolResultBlock from JSON +func TestAnthropicToolResultBlockLoadJSON(t *testing.T) { + jsonData := ` +{ + "tool_use_id": "toolu_01A09q90qw90lq917835lq9", + "content": "72°F and sunny in Paris" +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicToolResultBlock(data, ctx) + if err != nil { + t.Fatalf("Failed to load AnthropicToolResultBlock: %v", err) + } + if instance.ToolUseId != "toolu_01A09q90qw90lq917835lq9" { + t.Errorf(`Expected ToolUseId to be "toolu_01A09q90qw90lq917835lq9", got %v`, instance.ToolUseId) + } + if instance.Content != "72°F and sunny in Paris" { + t.Errorf(`Expected Content to be "72°F and sunny in Paris", got %v`, instance.Content) + } +} + +// TestAnthropicToolResultBlockLoadYAML tests loading AnthropicToolResultBlock from YAML +func TestAnthropicToolResultBlockLoadYAML(t *testing.T) { + yamlData := ` +tool_use_id: toolu_01A09q90qw90lq917835lq9 +content: 72°F and sunny in Paris + +` + var data map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlData), &data); err != nil { + t.Fatalf("Failed to parse YAML: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicToolResultBlock(data, ctx) + if err != nil { + t.Fatalf("Failed to load AnthropicToolResultBlock: %v", err) + } + if instance.ToolUseId != "toolu_01A09q90qw90lq917835lq9" { + t.Errorf(`Expected ToolUseId to be "toolu_01A09q90qw90lq917835lq9", got %v`, instance.ToolUseId) + } + if instance.Content != "72°F and sunny in Paris" { + t.Errorf(`Expected Content to be "72°F and sunny in Paris", got %v`, instance.Content) + } +} + +// TestAnthropicToolResultBlockRoundtrip tests load -> save -> load produces equivalent data +func TestAnthropicToolResultBlockRoundtrip(t *testing.T) { + jsonData := ` +{ + "tool_use_id": "toolu_01A09q90qw90lq917835lq9", + "content": "72°F and sunny in Paris" +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + loadCtx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicToolResultBlock(data, loadCtx) + if err != nil { + t.Fatalf("Failed to load AnthropicToolResultBlock: %v", err) + } + saveCtx := prompty.NewSaveContext() + savedData := instance.Save(saveCtx) + + reloaded, err := prompty.LoadAnthropicToolResultBlock(savedData, loadCtx) + if err != nil { + t.Fatalf("Failed to reload AnthropicToolResultBlock: %v", err) + } + if reloaded.ToolUseId != "toolu_01A09q90qw90lq917835lq9" { + t.Errorf(`Expected ToolUseId to be "toolu_01A09q90qw90lq917835lq9", got %v`, reloaded.ToolUseId) + } + if reloaded.Content != "72°F and sunny in Paris" { + t.Errorf(`Expected Content to be "72°F and sunny in Paris", got %v`, reloaded.Content) + } +} + +// TestAnthropicToolResultBlockToJSON tests that ToJSON produces valid JSON +func TestAnthropicToolResultBlockToJSON(t *testing.T) { + jsonData := ` +{ + "tool_use_id": "toolu_01A09q90qw90lq917835lq9", + "content": "72°F and sunny in Paris" +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicToolResultBlock(data, ctx) + if err != nil { + t.Fatalf("Failed to load AnthropicToolResultBlock: %v", err) + } + jsonOutput, err := instance.ToJSON() + if err != nil { + t.Fatalf("Failed to convert to JSON: %v", err) + } + + var parsed map[string]interface{} + if err := json.Unmarshal([]byte(jsonOutput), &parsed); err != nil { + t.Fatalf("Failed to parse generated JSON: %v", err) + } +} + +// TestAnthropicToolResultBlockToYAML tests that ToYAML produces valid YAML +func TestAnthropicToolResultBlockToYAML(t *testing.T) { + jsonData := ` +{ + "tool_use_id": "toolu_01A09q90qw90lq917835lq9", + "content": "72°F and sunny in Paris" +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicToolResultBlock(data, ctx) + if err != nil { + t.Fatalf("Failed to load AnthropicToolResultBlock: %v", err) + } + yamlOutput, err := instance.ToYAML() + if err != nil { + t.Fatalf("Failed to convert to YAML: %v", err) + } + + var parsed map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlOutput), &parsed); err != nil { + t.Fatalf("Failed to parse generated YAML: %v", err) + } +} diff --git a/runtime/go/prompty/model/anthropic_tool_use_block.go b/runtime/go/prompty/model/anthropic_tool_use_block.go new file mode 100644 index 00000000..3d60b7e8 --- /dev/null +++ b/runtime/go/prompty/model/anthropic_tool_use_block.go @@ -0,0 +1,98 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. +// Group: wire + +package prompty + +import ( + "encoding/json" + + "gopkg.in/yaml.v3" +) + +// AnthropicToolUseBlock represents A tool use content block returned in an assistant message when +// the model wants to invoke a tool. + +type AnthropicToolUseBlock struct { + Type string `json:"type" yaml:"type"` + Id string `json:"id" yaml:"id"` + Name string `json:"name" yaml:"name"` + Input map[string]interface{} `json:"input" yaml:"input"` +} + +// LoadAnthropicToolUseBlock creates a AnthropicToolUseBlock from a map[string]interface{} +func LoadAnthropicToolUseBlock(data interface{}, ctx *LoadContext) (AnthropicToolUseBlock, error) { + result := AnthropicToolUseBlock{} + + // Load from map + if m, ok := data.(map[string]interface{}); ok { + if val, ok := m["type"]; ok && val != nil { + result.Type = string(val.(string)) + } + if val, ok := m["id"]; ok && val != nil { + result.Id = string(val.(string)) + } + if val, ok := m["name"]; ok && val != nil { + result.Name = string(val.(string)) + } + if val, ok := m["input"]; ok && val != nil { + if m, ok := val.(map[string]interface{}); ok { + result.Input = m + } + } + } + + return result, nil +} + +// Save serializes AnthropicToolUseBlock to map[string]interface{} +func (obj *AnthropicToolUseBlock) Save(ctx *SaveContext) map[string]interface{} { + result := make(map[string]interface{}) + result["type"] = obj.Type + result["id"] = obj.Id + result["name"] = obj.Name + result["input"] = obj.Input + + return result +} + +// ToJSON serializes AnthropicToolUseBlock to JSON string +func (obj *AnthropicToolUseBlock) ToJSON() (string, error) { + ctx := NewSaveContext() + data := obj.Save(ctx) + bytes, err := json.Marshal(data) + if err != nil { + return "", err + } + return string(bytes), nil +} + +// ToYAML serializes AnthropicToolUseBlock to YAML string +func (obj *AnthropicToolUseBlock) ToYAML() (string, error) { + ctx := NewSaveContext() + data := obj.Save(ctx) + bytes, err := yaml.Marshal(data) + if err != nil { + return "", err + } + return string(bytes), nil +} + +// FromJSON creates AnthropicToolUseBlock from JSON string +func AnthropicToolUseBlockFromJSON(jsonStr string) (AnthropicToolUseBlock, error) { + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonStr), &data); err != nil { + return AnthropicToolUseBlock{}, err + } + ctx := NewLoadContext() + return LoadAnthropicToolUseBlock(data, ctx) +} + +// FromYAML creates AnthropicToolUseBlock from YAML string +func AnthropicToolUseBlockFromYAML(yamlStr string) (AnthropicToolUseBlock, error) { + var data map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlStr), &data); err != nil { + return AnthropicToolUseBlock{}, err + } + ctx := NewLoadContext() + return LoadAnthropicToolUseBlock(data, ctx) +} diff --git a/runtime/go/prompty/model/anthropic_tool_use_block_test.go b/runtime/go/prompty/model/anthropic_tool_use_block_test.go new file mode 100644 index 00000000..4e878bf5 --- /dev/null +++ b/runtime/go/prompty/model/anthropic_tool_use_block_test.go @@ -0,0 +1,168 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +package prompty_test + +import ( + "encoding/json" + "testing" + + "gopkg.in/yaml.v3" + + "prompty/model" +) + +// TestAnthropicToolUseBlockLoadJSON tests loading AnthropicToolUseBlock from JSON +func TestAnthropicToolUseBlockLoadJSON(t *testing.T) { + jsonData := ` +{ + "id": "toolu_01A09q90qw90lq917835lq9", + "name": "get_weather", + "input": { + "city": "Paris" + } +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicToolUseBlock(data, ctx) + if err != nil { + t.Fatalf("Failed to load AnthropicToolUseBlock: %v", err) + } + if instance.Id != "toolu_01A09q90qw90lq917835lq9" { + t.Errorf(`Expected Id to be "toolu_01A09q90qw90lq917835lq9", got %v`, instance.Id) + } + if instance.Name != "get_weather" { + t.Errorf(`Expected Name to be "get_weather", got %v`, instance.Name) + } +} + +// TestAnthropicToolUseBlockLoadYAML tests loading AnthropicToolUseBlock from YAML +func TestAnthropicToolUseBlockLoadYAML(t *testing.T) { + yamlData := ` +id: toolu_01A09q90qw90lq917835lq9 +name: get_weather +input: + city: Paris + +` + var data map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlData), &data); err != nil { + t.Fatalf("Failed to parse YAML: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicToolUseBlock(data, ctx) + if err != nil { + t.Fatalf("Failed to load AnthropicToolUseBlock: %v", err) + } + if instance.Id != "toolu_01A09q90qw90lq917835lq9" { + t.Errorf(`Expected Id to be "toolu_01A09q90qw90lq917835lq9", got %v`, instance.Id) + } + if instance.Name != "get_weather" { + t.Errorf(`Expected Name to be "get_weather", got %v`, instance.Name) + } +} + +// TestAnthropicToolUseBlockRoundtrip tests load -> save -> load produces equivalent data +func TestAnthropicToolUseBlockRoundtrip(t *testing.T) { + jsonData := ` +{ + "id": "toolu_01A09q90qw90lq917835lq9", + "name": "get_weather", + "input": { + "city": "Paris" + } +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + loadCtx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicToolUseBlock(data, loadCtx) + if err != nil { + t.Fatalf("Failed to load AnthropicToolUseBlock: %v", err) + } + saveCtx := prompty.NewSaveContext() + savedData := instance.Save(saveCtx) + + reloaded, err := prompty.LoadAnthropicToolUseBlock(savedData, loadCtx) + if err != nil { + t.Fatalf("Failed to reload AnthropicToolUseBlock: %v", err) + } + if reloaded.Id != "toolu_01A09q90qw90lq917835lq9" { + t.Errorf(`Expected Id to be "toolu_01A09q90qw90lq917835lq9", got %v`, reloaded.Id) + } + if reloaded.Name != "get_weather" { + t.Errorf(`Expected Name to be "get_weather", got %v`, reloaded.Name) + } +} + +// TestAnthropicToolUseBlockToJSON tests that ToJSON produces valid JSON +func TestAnthropicToolUseBlockToJSON(t *testing.T) { + jsonData := ` +{ + "id": "toolu_01A09q90qw90lq917835lq9", + "name": "get_weather", + "input": { + "city": "Paris" + } +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicToolUseBlock(data, ctx) + if err != nil { + t.Fatalf("Failed to load AnthropicToolUseBlock: %v", err) + } + jsonOutput, err := instance.ToJSON() + if err != nil { + t.Fatalf("Failed to convert to JSON: %v", err) + } + + var parsed map[string]interface{} + if err := json.Unmarshal([]byte(jsonOutput), &parsed); err != nil { + t.Fatalf("Failed to parse generated JSON: %v", err) + } +} + +// TestAnthropicToolUseBlockToYAML tests that ToYAML produces valid YAML +func TestAnthropicToolUseBlockToYAML(t *testing.T) { + jsonData := ` +{ + "id": "toolu_01A09q90qw90lq917835lq9", + "name": "get_weather", + "input": { + "city": "Paris" + } +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicToolUseBlock(data, ctx) + if err != nil { + t.Fatalf("Failed to load AnthropicToolUseBlock: %v", err) + } + yamlOutput, err := instance.ToYAML() + if err != nil { + t.Fatalf("Failed to convert to YAML: %v", err) + } + + var parsed map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlOutput), &parsed); err != nil { + t.Fatalf("Failed to parse generated YAML: %v", err) + } +} diff --git a/runtime/go/prompty/model/anthropic_usage.go b/runtime/go/prompty/model/anthropic_usage.go new file mode 100644 index 00000000..5a38cd5f --- /dev/null +++ b/runtime/go/prompty/model/anthropic_usage.go @@ -0,0 +1,107 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. +// Group: wire + +package prompty + +import ( + "encoding/json" + + "gopkg.in/yaml.v3" +) + +// AnthropicUsage represents Usage statistics returned in an Anthropic Messages API response. + +type AnthropicUsage struct { + InputTokens int32 `json:"input_tokens" yaml:"input_tokens"` + OutputTokens int32 `json:"output_tokens" yaml:"output_tokens"` +} + +// LoadAnthropicUsage creates a AnthropicUsage from a map[string]interface{} +func LoadAnthropicUsage(data interface{}, ctx *LoadContext) (AnthropicUsage, error) { + result := AnthropicUsage{} + + // Load from map + if m, ok := data.(map[string]interface{}); ok { + if val, ok := m["input_tokens"]; ok && val != nil { // Handle various numeric types from JSON/YAML/roundtrip + var v int32 + switch n := val.(type) { + case int: + v = int32(n) + case int32: + v = int32(n) + case int64: + v = int32(n) + case float64: + v = int32(n) + } + result.InputTokens = v + } + if val, ok := m["output_tokens"]; ok && val != nil { // Handle various numeric types from JSON/YAML/roundtrip + var v int32 + switch n := val.(type) { + case int: + v = int32(n) + case int32: + v = int32(n) + case int64: + v = int32(n) + case float64: + v = int32(n) + } + result.OutputTokens = v + } + } + + return result, nil +} + +// Save serializes AnthropicUsage to map[string]interface{} +func (obj *AnthropicUsage) Save(ctx *SaveContext) map[string]interface{} { + result := make(map[string]interface{}) + result["input_tokens"] = obj.InputTokens + result["output_tokens"] = obj.OutputTokens + + return result +} + +// ToJSON serializes AnthropicUsage to JSON string +func (obj *AnthropicUsage) ToJSON() (string, error) { + ctx := NewSaveContext() + data := obj.Save(ctx) + bytes, err := json.Marshal(data) + if err != nil { + return "", err + } + return string(bytes), nil +} + +// ToYAML serializes AnthropicUsage to YAML string +func (obj *AnthropicUsage) ToYAML() (string, error) { + ctx := NewSaveContext() + data := obj.Save(ctx) + bytes, err := yaml.Marshal(data) + if err != nil { + return "", err + } + return string(bytes), nil +} + +// FromJSON creates AnthropicUsage from JSON string +func AnthropicUsageFromJSON(jsonStr string) (AnthropicUsage, error) { + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonStr), &data); err != nil { + return AnthropicUsage{}, err + } + ctx := NewLoadContext() + return LoadAnthropicUsage(data, ctx) +} + +// FromYAML creates AnthropicUsage from YAML string +func AnthropicUsageFromYAML(yamlStr string) (AnthropicUsage, error) { + var data map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlStr), &data); err != nil { + return AnthropicUsage{}, err + } + ctx := NewLoadContext() + return LoadAnthropicUsage(data, ctx) +} diff --git a/runtime/go/prompty/model/anthropic_usage_test.go b/runtime/go/prompty/model/anthropic_usage_test.go new file mode 100644 index 00000000..e34fd9f5 --- /dev/null +++ b/runtime/go/prompty/model/anthropic_usage_test.go @@ -0,0 +1,154 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +package prompty_test + +import ( + "encoding/json" + "testing" + + "gopkg.in/yaml.v3" + + "prompty/model" +) + +// TestAnthropicUsageLoadJSON tests loading AnthropicUsage from JSON +func TestAnthropicUsageLoadJSON(t *testing.T) { + jsonData := ` +{ + "input_tokens": 150, + "output_tokens": 42 +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicUsage(data, ctx) + if err != nil { + t.Fatalf("Failed to load AnthropicUsage: %v", err) + } + if instance.InputTokens != 150 { + t.Errorf(`Expected InputTokens to be 150, got %v`, instance.InputTokens) + } + if instance.OutputTokens != 42 { + t.Errorf(`Expected OutputTokens to be 42, got %v`, instance.OutputTokens) + } +} + +// TestAnthropicUsageLoadYAML tests loading AnthropicUsage from YAML +func TestAnthropicUsageLoadYAML(t *testing.T) { + yamlData := ` +input_tokens: 150 +output_tokens: 42 + +` + var data map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlData), &data); err != nil { + t.Fatalf("Failed to parse YAML: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicUsage(data, ctx) + if err != nil { + t.Fatalf("Failed to load AnthropicUsage: %v", err) + } + if instance.InputTokens != 150 { + t.Errorf(`Expected InputTokens to be 150, got %v`, instance.InputTokens) + } + if instance.OutputTokens != 42 { + t.Errorf(`Expected OutputTokens to be 42, got %v`, instance.OutputTokens) + } +} + +// TestAnthropicUsageRoundtrip tests load -> save -> load produces equivalent data +func TestAnthropicUsageRoundtrip(t *testing.T) { + jsonData := ` +{ + "input_tokens": 150, + "output_tokens": 42 +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + loadCtx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicUsage(data, loadCtx) + if err != nil { + t.Fatalf("Failed to load AnthropicUsage: %v", err) + } + saveCtx := prompty.NewSaveContext() + savedData := instance.Save(saveCtx) + + reloaded, err := prompty.LoadAnthropicUsage(savedData, loadCtx) + if err != nil { + t.Fatalf("Failed to reload AnthropicUsage: %v", err) + } + if reloaded.InputTokens != 150 { + t.Errorf(`Expected InputTokens to be 150, got %v`, reloaded.InputTokens) + } + if reloaded.OutputTokens != 42 { + t.Errorf(`Expected OutputTokens to be 42, got %v`, reloaded.OutputTokens) + } +} + +// TestAnthropicUsageToJSON tests that ToJSON produces valid JSON +func TestAnthropicUsageToJSON(t *testing.T) { + jsonData := ` +{ + "input_tokens": 150, + "output_tokens": 42 +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicUsage(data, ctx) + if err != nil { + t.Fatalf("Failed to load AnthropicUsage: %v", err) + } + jsonOutput, err := instance.ToJSON() + if err != nil { + t.Fatalf("Failed to convert to JSON: %v", err) + } + + var parsed map[string]interface{} + if err := json.Unmarshal([]byte(jsonOutput), &parsed); err != nil { + t.Fatalf("Failed to parse generated JSON: %v", err) + } +} + +// TestAnthropicUsageToYAML tests that ToYAML produces valid YAML +func TestAnthropicUsageToYAML(t *testing.T) { + jsonData := ` +{ + "input_tokens": 150, + "output_tokens": 42 +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicUsage(data, ctx) + if err != nil { + t.Fatalf("Failed to load AnthropicUsage: %v", err) + } + yamlOutput, err := instance.ToYAML() + if err != nil { + t.Fatalf("Failed to convert to YAML: %v", err) + } + + var parsed map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlOutput), &parsed); err != nil { + t.Fatalf("Failed to parse generated YAML: %v", err) + } +} diff --git a/runtime/go/prompty/model/anthropic_wire_message.go b/runtime/go/prompty/model/anthropic_wire_message.go new file mode 100644 index 00000000..51d3e5db --- /dev/null +++ b/runtime/go/prompty/model/anthropic_wire_message.go @@ -0,0 +1,89 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. +// Group: wire + +package prompty + +import ( + "encoding/json" + + "gopkg.in/yaml.v3" +) + +// AnthropicWireMessage represents A single message in the Anthropic Messages API wire format. +// Anthropic always uses the array-of-blocks form for content, +// even when there is only one text block (§7.5). + +type AnthropicWireMessage struct { + Role string `json:"role" yaml:"role"` + Content []interface{} `json:"content" yaml:"content"` +} + +// LoadAnthropicWireMessage creates a AnthropicWireMessage from a map[string]interface{} +func LoadAnthropicWireMessage(data interface{}, ctx *LoadContext) (AnthropicWireMessage, error) { + result := AnthropicWireMessage{} + + // Load from map + if m, ok := data.(map[string]interface{}); ok { + if val, ok := m["role"]; ok && val != nil { + result.Role = string(val.(string)) + } + if val, ok := m["content"]; ok && val != nil { + if arr, ok := val.([]interface{}); ok { + result.Content = arr + } + } + } + + return result, nil +} + +// Save serializes AnthropicWireMessage to map[string]interface{} +func (obj *AnthropicWireMessage) Save(ctx *SaveContext) map[string]interface{} { + result := make(map[string]interface{}) + result["role"] = obj.Role + result["content"] = obj.Content + + return result +} + +// ToJSON serializes AnthropicWireMessage to JSON string +func (obj *AnthropicWireMessage) ToJSON() (string, error) { + ctx := NewSaveContext() + data := obj.Save(ctx) + bytes, err := json.Marshal(data) + if err != nil { + return "", err + } + return string(bytes), nil +} + +// ToYAML serializes AnthropicWireMessage to YAML string +func (obj *AnthropicWireMessage) ToYAML() (string, error) { + ctx := NewSaveContext() + data := obj.Save(ctx) + bytes, err := yaml.Marshal(data) + if err != nil { + return "", err + } + return string(bytes), nil +} + +// FromJSON creates AnthropicWireMessage from JSON string +func AnthropicWireMessageFromJSON(jsonStr string) (AnthropicWireMessage, error) { + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonStr), &data); err != nil { + return AnthropicWireMessage{}, err + } + ctx := NewLoadContext() + return LoadAnthropicWireMessage(data, ctx) +} + +// FromYAML creates AnthropicWireMessage from YAML string +func AnthropicWireMessageFromYAML(yamlStr string) (AnthropicWireMessage, error) { + var data map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlStr), &data); err != nil { + return AnthropicWireMessage{}, err + } + ctx := NewLoadContext() + return LoadAnthropicWireMessage(data, ctx) +} diff --git a/runtime/go/prompty/model/anthropic_wire_message_test.go b/runtime/go/prompty/model/anthropic_wire_message_test.go new file mode 100644 index 00000000..42ac973b --- /dev/null +++ b/runtime/go/prompty/model/anthropic_wire_message_test.go @@ -0,0 +1,140 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +package prompty_test + +import ( + "encoding/json" + "testing" + + "gopkg.in/yaml.v3" + + "prompty/model" +) + +// TestAnthropicWireMessageLoadJSON tests loading AnthropicWireMessage from JSON +func TestAnthropicWireMessageLoadJSON(t *testing.T) { + jsonData := ` +{ + "role": "user" +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicWireMessage(data, ctx) + if err != nil { + t.Fatalf("Failed to load AnthropicWireMessage: %v", err) + } + if instance.Role != "user" { + t.Errorf(`Expected Role to be "user", got %v`, instance.Role) + } +} + +// TestAnthropicWireMessageLoadYAML tests loading AnthropicWireMessage from YAML +func TestAnthropicWireMessageLoadYAML(t *testing.T) { + yamlData := ` +role: user + +` + var data map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlData), &data); err != nil { + t.Fatalf("Failed to parse YAML: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicWireMessage(data, ctx) + if err != nil { + t.Fatalf("Failed to load AnthropicWireMessage: %v", err) + } + if instance.Role != "user" { + t.Errorf(`Expected Role to be "user", got %v`, instance.Role) + } +} + +// TestAnthropicWireMessageRoundtrip tests load -> save -> load produces equivalent data +func TestAnthropicWireMessageRoundtrip(t *testing.T) { + jsonData := ` +{ + "role": "user" +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + loadCtx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicWireMessage(data, loadCtx) + if err != nil { + t.Fatalf("Failed to load AnthropicWireMessage: %v", err) + } + saveCtx := prompty.NewSaveContext() + savedData := instance.Save(saveCtx) + + reloaded, err := prompty.LoadAnthropicWireMessage(savedData, loadCtx) + if err != nil { + t.Fatalf("Failed to reload AnthropicWireMessage: %v", err) + } + if reloaded.Role != "user" { + t.Errorf(`Expected Role to be "user", got %v`, reloaded.Role) + } +} + +// TestAnthropicWireMessageToJSON tests that ToJSON produces valid JSON +func TestAnthropicWireMessageToJSON(t *testing.T) { + jsonData := ` +{ + "role": "user" +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicWireMessage(data, ctx) + if err != nil { + t.Fatalf("Failed to load AnthropicWireMessage: %v", err) + } + jsonOutput, err := instance.ToJSON() + if err != nil { + t.Fatalf("Failed to convert to JSON: %v", err) + } + + var parsed map[string]interface{} + if err := json.Unmarshal([]byte(jsonOutput), &parsed); err != nil { + t.Fatalf("Failed to parse generated JSON: %v", err) + } +} + +// TestAnthropicWireMessageToYAML tests that ToYAML produces valid YAML +func TestAnthropicWireMessageToYAML(t *testing.T) { + jsonData := ` +{ + "role": "user" +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadAnthropicWireMessage(data, ctx) + if err != nil { + t.Fatalf("Failed to load AnthropicWireMessage: %v", err) + } + yamlOutput, err := instance.ToYAML() + if err != nil { + t.Fatalf("Failed to convert to YAML: %v", err) + } + + var parsed map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlOutput), &parsed); err != nil { + t.Fatalf("Failed to parse generated YAML: %v", err) + } +} diff --git a/runtime/go/prompty/model/file_not_found_error.go b/runtime/go/prompty/model/file_not_found_error.go new file mode 100644 index 00000000..5604cba8 --- /dev/null +++ b/runtime/go/prompty/model/file_not_found_error.go @@ -0,0 +1,86 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. +// Group: core + +package prompty + +import ( + "encoding/json" + + "gopkg.in/yaml.v3" +) + +// FileNotFoundError represents Raised when a referenced file cannot be found. This applies to both +// .prompty files and ${file:path} references in frontmatter. + +type FileNotFoundError struct { + Message string `json:"message" yaml:"message"` + Path string `json:"path" yaml:"path"` +} + +// LoadFileNotFoundError creates a FileNotFoundError from a map[string]interface{} +func LoadFileNotFoundError(data interface{}, ctx *LoadContext) (FileNotFoundError, error) { + result := FileNotFoundError{} + + // Load from map + if m, ok := data.(map[string]interface{}); ok { + if val, ok := m["message"]; ok && val != nil { + result.Message = string(val.(string)) + } + if val, ok := m["path"]; ok && val != nil { + result.Path = string(val.(string)) + } + } + + return result, nil +} + +// Save serializes FileNotFoundError to map[string]interface{} +func (obj *FileNotFoundError) Save(ctx *SaveContext) map[string]interface{} { + result := make(map[string]interface{}) + result["message"] = obj.Message + result["path"] = obj.Path + + return result +} + +// ToJSON serializes FileNotFoundError to JSON string +func (obj *FileNotFoundError) ToJSON() (string, error) { + ctx := NewSaveContext() + data := obj.Save(ctx) + bytes, err := json.Marshal(data) + if err != nil { + return "", err + } + return string(bytes), nil +} + +// ToYAML serializes FileNotFoundError to YAML string +func (obj *FileNotFoundError) ToYAML() (string, error) { + ctx := NewSaveContext() + data := obj.Save(ctx) + bytes, err := yaml.Marshal(data) + if err != nil { + return "", err + } + return string(bytes), nil +} + +// FromJSON creates FileNotFoundError from JSON string +func FileNotFoundErrorFromJSON(jsonStr string) (FileNotFoundError, error) { + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonStr), &data); err != nil { + return FileNotFoundError{}, err + } + ctx := NewLoadContext() + return LoadFileNotFoundError(data, ctx) +} + +// FromYAML creates FileNotFoundError from YAML string +func FileNotFoundErrorFromYAML(yamlStr string) (FileNotFoundError, error) { + var data map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlStr), &data); err != nil { + return FileNotFoundError{}, err + } + ctx := NewLoadContext() + return LoadFileNotFoundError(data, ctx) +} diff --git a/runtime/go/prompty/model/file_not_found_error_test.go b/runtime/go/prompty/model/file_not_found_error_test.go new file mode 100644 index 00000000..cc262db6 --- /dev/null +++ b/runtime/go/prompty/model/file_not_found_error_test.go @@ -0,0 +1,154 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +package prompty_test + +import ( + "encoding/json" + "testing" + + "gopkg.in/yaml.v3" + + "prompty/model" +) + +// TestFileNotFoundErrorLoadJSON tests loading FileNotFoundError from JSON +func TestFileNotFoundErrorLoadJSON(t *testing.T) { + jsonData := ` +{ + "message": "Prompty file not found: ./chat.prompty", + "path": "./chat.prompty" +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadFileNotFoundError(data, ctx) + if err != nil { + t.Fatalf("Failed to load FileNotFoundError: %v", err) + } + if instance.Message != "Prompty file not found: ./chat.prompty" { + t.Errorf(`Expected Message to be "Prompty file not found: ./chat.prompty", got %v`, instance.Message) + } + if instance.Path != "./chat.prompty" { + t.Errorf(`Expected Path to be "./chat.prompty", got %v`, instance.Path) + } +} + +// TestFileNotFoundErrorLoadYAML tests loading FileNotFoundError from YAML +func TestFileNotFoundErrorLoadYAML(t *testing.T) { + yamlData := ` +message: "Prompty file not found: ./chat.prompty" +path: ./chat.prompty + +` + var data map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlData), &data); err != nil { + t.Fatalf("Failed to parse YAML: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadFileNotFoundError(data, ctx) + if err != nil { + t.Fatalf("Failed to load FileNotFoundError: %v", err) + } + if instance.Message != "Prompty file not found: ./chat.prompty" { + t.Errorf(`Expected Message to be "Prompty file not found: ./chat.prompty", got %v`, instance.Message) + } + if instance.Path != "./chat.prompty" { + t.Errorf(`Expected Path to be "./chat.prompty", got %v`, instance.Path) + } +} + +// TestFileNotFoundErrorRoundtrip tests load -> save -> load produces equivalent data +func TestFileNotFoundErrorRoundtrip(t *testing.T) { + jsonData := ` +{ + "message": "Prompty file not found: ./chat.prompty", + "path": "./chat.prompty" +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + loadCtx := prompty.NewLoadContext() + instance, err := prompty.LoadFileNotFoundError(data, loadCtx) + if err != nil { + t.Fatalf("Failed to load FileNotFoundError: %v", err) + } + saveCtx := prompty.NewSaveContext() + savedData := instance.Save(saveCtx) + + reloaded, err := prompty.LoadFileNotFoundError(savedData, loadCtx) + if err != nil { + t.Fatalf("Failed to reload FileNotFoundError: %v", err) + } + if reloaded.Message != "Prompty file not found: ./chat.prompty" { + t.Errorf(`Expected Message to be "Prompty file not found: ./chat.prompty", got %v`, reloaded.Message) + } + if reloaded.Path != "./chat.prompty" { + t.Errorf(`Expected Path to be "./chat.prompty", got %v`, reloaded.Path) + } +} + +// TestFileNotFoundErrorToJSON tests that ToJSON produces valid JSON +func TestFileNotFoundErrorToJSON(t *testing.T) { + jsonData := ` +{ + "message": "Prompty file not found: ./chat.prompty", + "path": "./chat.prompty" +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadFileNotFoundError(data, ctx) + if err != nil { + t.Fatalf("Failed to load FileNotFoundError: %v", err) + } + jsonOutput, err := instance.ToJSON() + if err != nil { + t.Fatalf("Failed to convert to JSON: %v", err) + } + + var parsed map[string]interface{} + if err := json.Unmarshal([]byte(jsonOutput), &parsed); err != nil { + t.Fatalf("Failed to parse generated JSON: %v", err) + } +} + +// TestFileNotFoundErrorToYAML tests that ToYAML produces valid YAML +func TestFileNotFoundErrorToYAML(t *testing.T) { + jsonData := ` +{ + "message": "Prompty file not found: ./chat.prompty", + "path": "./chat.prompty" +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadFileNotFoundError(data, ctx) + if err != nil { + t.Fatalf("Failed to load FileNotFoundError: %v", err) + } + yamlOutput, err := instance.ToYAML() + if err != nil { + t.Fatalf("Failed to convert to YAML: %v", err) + } + + var parsed map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlOutput), &parsed); err != nil { + t.Fatalf("Failed to parse generated YAML: %v", err) + } +} diff --git a/runtime/go/prompty/model/invoker_error.go b/runtime/go/prompty/model/invoker_error.go new file mode 100644 index 00000000..d1340f58 --- /dev/null +++ b/runtime/go/prompty/model/invoker_error.go @@ -0,0 +1,92 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. +// Group: core + +package prompty + +import ( + "encoding/json" + + "gopkg.in/yaml.v3" +) + +// InvokerError represents Raised when no invoker implementation is registered for a given component +// and key. For example, if no renderer is registered for the key "jinja2", +// an InvokerError is raised. + +type InvokerError struct { + Message string `json:"message" yaml:"message"` + Component string `json:"component" yaml:"component"` + Key string `json:"key" yaml:"key"` +} + +// LoadInvokerError creates a InvokerError from a map[string]interface{} +func LoadInvokerError(data interface{}, ctx *LoadContext) (InvokerError, error) { + result := InvokerError{} + + // Load from map + if m, ok := data.(map[string]interface{}); ok { + if val, ok := m["message"]; ok && val != nil { + result.Message = string(val.(string)) + } + if val, ok := m["component"]; ok && val != nil { + result.Component = string(val.(string)) + } + if val, ok := m["key"]; ok && val != nil { + result.Key = string(val.(string)) + } + } + + return result, nil +} + +// Save serializes InvokerError to map[string]interface{} +func (obj *InvokerError) Save(ctx *SaveContext) map[string]interface{} { + result := make(map[string]interface{}) + result["message"] = obj.Message + result["component"] = obj.Component + result["key"] = obj.Key + + return result +} + +// ToJSON serializes InvokerError to JSON string +func (obj *InvokerError) ToJSON() (string, error) { + ctx := NewSaveContext() + data := obj.Save(ctx) + bytes, err := json.Marshal(data) + if err != nil { + return "", err + } + return string(bytes), nil +} + +// ToYAML serializes InvokerError to YAML string +func (obj *InvokerError) ToYAML() (string, error) { + ctx := NewSaveContext() + data := obj.Save(ctx) + bytes, err := yaml.Marshal(data) + if err != nil { + return "", err + } + return string(bytes), nil +} + +// FromJSON creates InvokerError from JSON string +func InvokerErrorFromJSON(jsonStr string) (InvokerError, error) { + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonStr), &data); err != nil { + return InvokerError{}, err + } + ctx := NewLoadContext() + return LoadInvokerError(data, ctx) +} + +// FromYAML creates InvokerError from YAML string +func InvokerErrorFromYAML(yamlStr string) (InvokerError, error) { + var data map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlStr), &data); err != nil { + return InvokerError{}, err + } + ctx := NewLoadContext() + return LoadInvokerError(data, ctx) +} diff --git a/runtime/go/prompty/model/invoker_error_test.go b/runtime/go/prompty/model/invoker_error_test.go new file mode 100644 index 00000000..d30b6843 --- /dev/null +++ b/runtime/go/prompty/model/invoker_error_test.go @@ -0,0 +1,168 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +package prompty_test + +import ( + "encoding/json" + "testing" + + "gopkg.in/yaml.v3" + + "prompty/model" +) + +// TestInvokerErrorLoadJSON tests loading InvokerError from JSON +func TestInvokerErrorLoadJSON(t *testing.T) { + jsonData := ` +{ + "message": "No renderer registered for key: jinja2", + "component": "renderer", + "key": "jinja2" +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadInvokerError(data, ctx) + if err != nil { + t.Fatalf("Failed to load InvokerError: %v", err) + } + if instance.Message != "No renderer registered for key: jinja2" { + t.Errorf(`Expected Message to be "No renderer registered for key: jinja2", got %v`, instance.Message) + } + if instance.Component != "renderer" { + t.Errorf(`Expected Component to be "renderer", got %v`, instance.Component) + } + if instance.Key != "jinja2" { + t.Errorf(`Expected Key to be "jinja2", got %v`, instance.Key) + } +} + +// TestInvokerErrorLoadYAML tests loading InvokerError from YAML +func TestInvokerErrorLoadYAML(t *testing.T) { + yamlData := ` +message: "No renderer registered for key: jinja2" +component: renderer +key: jinja2 + +` + var data map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlData), &data); err != nil { + t.Fatalf("Failed to parse YAML: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadInvokerError(data, ctx) + if err != nil { + t.Fatalf("Failed to load InvokerError: %v", err) + } + if instance.Message != "No renderer registered for key: jinja2" { + t.Errorf(`Expected Message to be "No renderer registered for key: jinja2", got %v`, instance.Message) + } + if instance.Component != "renderer" { + t.Errorf(`Expected Component to be "renderer", got %v`, instance.Component) + } + if instance.Key != "jinja2" { + t.Errorf(`Expected Key to be "jinja2", got %v`, instance.Key) + } +} + +// TestInvokerErrorRoundtrip tests load -> save -> load produces equivalent data +func TestInvokerErrorRoundtrip(t *testing.T) { + jsonData := ` +{ + "message": "No renderer registered for key: jinja2", + "component": "renderer", + "key": "jinja2" +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + loadCtx := prompty.NewLoadContext() + instance, err := prompty.LoadInvokerError(data, loadCtx) + if err != nil { + t.Fatalf("Failed to load InvokerError: %v", err) + } + saveCtx := prompty.NewSaveContext() + savedData := instance.Save(saveCtx) + + reloaded, err := prompty.LoadInvokerError(savedData, loadCtx) + if err != nil { + t.Fatalf("Failed to reload InvokerError: %v", err) + } + if reloaded.Message != "No renderer registered for key: jinja2" { + t.Errorf(`Expected Message to be "No renderer registered for key: jinja2", got %v`, reloaded.Message) + } + if reloaded.Component != "renderer" { + t.Errorf(`Expected Component to be "renderer", got %v`, reloaded.Component) + } + if reloaded.Key != "jinja2" { + t.Errorf(`Expected Key to be "jinja2", got %v`, reloaded.Key) + } +} + +// TestInvokerErrorToJSON tests that ToJSON produces valid JSON +func TestInvokerErrorToJSON(t *testing.T) { + jsonData := ` +{ + "message": "No renderer registered for key: jinja2", + "component": "renderer", + "key": "jinja2" +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadInvokerError(data, ctx) + if err != nil { + t.Fatalf("Failed to load InvokerError: %v", err) + } + jsonOutput, err := instance.ToJSON() + if err != nil { + t.Fatalf("Failed to convert to JSON: %v", err) + } + + var parsed map[string]interface{} + if err := json.Unmarshal([]byte(jsonOutput), &parsed); err != nil { + t.Fatalf("Failed to parse generated JSON: %v", err) + } +} + +// TestInvokerErrorToYAML tests that ToYAML produces valid YAML +func TestInvokerErrorToYAML(t *testing.T) { + jsonData := ` +{ + "message": "No renderer registered for key: jinja2", + "component": "renderer", + "key": "jinja2" +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadInvokerError(data, ctx) + if err != nil { + t.Fatalf("Failed to load InvokerError: %v", err) + } + yamlOutput, err := instance.ToYAML() + if err != nil { + t.Fatalf("Failed to convert to YAML: %v", err) + } + + var parsed map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlOutput), &parsed); err != nil { + t.Fatalf("Failed to parse generated YAML: %v", err) + } +} diff --git a/runtime/go/prompty/model/stream_options.go b/runtime/go/prompty/model/stream_options.go new file mode 100644 index 00000000..82fe6a44 --- /dev/null +++ b/runtime/go/prompty/model/stream_options.go @@ -0,0 +1,84 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. +// Group: streaming + +package prompty + +import ( + "encoding/json" + + "gopkg.in/yaml.v3" +) + +// StreamOptions represents Options controlling streaming behavior for LLM API calls. +// Passed alongside the model options when streaming is enabled. + +type StreamOptions struct { + IncludeUsage *bool `json:"includeUsage,omitempty" yaml:"includeUsage,omitempty"` +} + +// LoadStreamOptions creates a StreamOptions from a map[string]interface{} +func LoadStreamOptions(data interface{}, ctx *LoadContext) (StreamOptions, error) { + result := StreamOptions{} + + // Load from map + if m, ok := data.(map[string]interface{}); ok { + if val, ok := m["includeUsage"]; ok && val != nil { + v := val.(bool) + result.IncludeUsage = &v + } + } + + return result, nil +} + +// Save serializes StreamOptions to map[string]interface{} +func (obj *StreamOptions) Save(ctx *SaveContext) map[string]interface{} { + result := make(map[string]interface{}) + if obj.IncludeUsage != nil { + result["includeUsage"] = *obj.IncludeUsage + } + + return result +} + +// ToJSON serializes StreamOptions to JSON string +func (obj *StreamOptions) ToJSON() (string, error) { + ctx := NewSaveContext() + data := obj.Save(ctx) + bytes, err := json.Marshal(data) + if err != nil { + return "", err + } + return string(bytes), nil +} + +// ToYAML serializes StreamOptions to YAML string +func (obj *StreamOptions) ToYAML() (string, error) { + ctx := NewSaveContext() + data := obj.Save(ctx) + bytes, err := yaml.Marshal(data) + if err != nil { + return "", err + } + return string(bytes), nil +} + +// FromJSON creates StreamOptions from JSON string +func StreamOptionsFromJSON(jsonStr string) (StreamOptions, error) { + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonStr), &data); err != nil { + return StreamOptions{}, err + } + ctx := NewLoadContext() + return LoadStreamOptions(data, ctx) +} + +// FromYAML creates StreamOptions from YAML string +func StreamOptionsFromYAML(yamlStr string) (StreamOptions, error) { + var data map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlStr), &data); err != nil { + return StreamOptions{}, err + } + ctx := NewLoadContext() + return LoadStreamOptions(data, ctx) +} diff --git a/runtime/go/prompty/model/stream_options_test.go b/runtime/go/prompty/model/stream_options_test.go new file mode 100644 index 00000000..6acbd5a5 --- /dev/null +++ b/runtime/go/prompty/model/stream_options_test.go @@ -0,0 +1,140 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +package prompty_test + +import ( + "encoding/json" + "testing" + + "gopkg.in/yaml.v3" + + "prompty/model" +) + +// TestStreamOptionsLoadJSON tests loading StreamOptions from JSON +func TestStreamOptionsLoadJSON(t *testing.T) { + jsonData := ` +{ + "includeUsage": true +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadStreamOptions(data, ctx) + if err != nil { + t.Fatalf("Failed to load StreamOptions: %v", err) + } + if instance.IncludeUsage == nil || *instance.IncludeUsage != true { + t.Errorf(`Expected IncludeUsage to be true, got %v`, instance.IncludeUsage) + } +} + +// TestStreamOptionsLoadYAML tests loading StreamOptions from YAML +func TestStreamOptionsLoadYAML(t *testing.T) { + yamlData := ` +includeUsage: true + +` + var data map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlData), &data); err != nil { + t.Fatalf("Failed to parse YAML: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadStreamOptions(data, ctx) + if err != nil { + t.Fatalf("Failed to load StreamOptions: %v", err) + } + if instance.IncludeUsage == nil || *instance.IncludeUsage != true { + t.Errorf(`Expected IncludeUsage to be true, got %v`, instance.IncludeUsage) + } +} + +// TestStreamOptionsRoundtrip tests load -> save -> load produces equivalent data +func TestStreamOptionsRoundtrip(t *testing.T) { + jsonData := ` +{ + "includeUsage": true +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + loadCtx := prompty.NewLoadContext() + instance, err := prompty.LoadStreamOptions(data, loadCtx) + if err != nil { + t.Fatalf("Failed to load StreamOptions: %v", err) + } + saveCtx := prompty.NewSaveContext() + savedData := instance.Save(saveCtx) + + reloaded, err := prompty.LoadStreamOptions(savedData, loadCtx) + if err != nil { + t.Fatalf("Failed to reload StreamOptions: %v", err) + } + if reloaded.IncludeUsage == nil || *reloaded.IncludeUsage != true { + t.Errorf(`Expected IncludeUsage to be true, got %v`, reloaded.IncludeUsage) + } +} + +// TestStreamOptionsToJSON tests that ToJSON produces valid JSON +func TestStreamOptionsToJSON(t *testing.T) { + jsonData := ` +{ + "includeUsage": true +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadStreamOptions(data, ctx) + if err != nil { + t.Fatalf("Failed to load StreamOptions: %v", err) + } + jsonOutput, err := instance.ToJSON() + if err != nil { + t.Fatalf("Failed to convert to JSON: %v", err) + } + + var parsed map[string]interface{} + if err := json.Unmarshal([]byte(jsonOutput), &parsed); err != nil { + t.Fatalf("Failed to parse generated JSON: %v", err) + } +} + +// TestStreamOptionsToYAML tests that ToYAML produces valid YAML +func TestStreamOptionsToYAML(t *testing.T) { + jsonData := ` +{ + "includeUsage": true +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadStreamOptions(data, ctx) + if err != nil { + t.Fatalf("Failed to load StreamOptions: %v", err) + } + yamlOutput, err := instance.ToYAML() + if err != nil { + t.Fatalf("Failed to convert to YAML: %v", err) + } + + var parsed map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlOutput), &parsed); err != nil { + t.Fatalf("Failed to parse generated YAML: %v", err) + } +} diff --git a/runtime/go/prompty/model/trace_file.go b/runtime/go/prompty/model/trace_file.go new file mode 100644 index 00000000..ee5aa296 --- /dev/null +++ b/runtime/go/prompty/model/trace_file.go @@ -0,0 +1,94 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. +// Group: tracing + +package prompty + +import ( + "encoding/json" + + "gopkg.in/yaml.v3" +) + +// TraceFile represents The top-level .tracy file structure written by the file backend (§3.6.1). + +type TraceFile struct { + Runtime string `json:"runtime" yaml:"runtime"` + Version string `json:"version" yaml:"version"` + Trace TraceSpan `json:"trace" yaml:"trace"` +} + +// LoadTraceFile creates a TraceFile from a map[string]interface{} +func LoadTraceFile(data interface{}, ctx *LoadContext) (TraceFile, error) { + result := TraceFile{} + + // Load from map + if m, ok := data.(map[string]interface{}); ok { + if val, ok := m["runtime"]; ok && val != nil { + result.Runtime = string(val.(string)) + } + if val, ok := m["version"]; ok && val != nil { + result.Version = string(val.(string)) + } + if val, ok := m["trace"]; ok && val != nil { + if m, ok := val.(map[string]interface{}); ok { + loaded, _ := LoadTraceSpan(m, ctx) + result.Trace = loaded + } + } + } + + return result, nil +} + +// Save serializes TraceFile to map[string]interface{} +func (obj *TraceFile) Save(ctx *SaveContext) map[string]interface{} { + result := make(map[string]interface{}) + result["runtime"] = obj.Runtime + result["version"] = obj.Version + + result["trace"] = obj.Trace.Save(ctx) + + return result +} + +// ToJSON serializes TraceFile to JSON string +func (obj *TraceFile) ToJSON() (string, error) { + ctx := NewSaveContext() + data := obj.Save(ctx) + bytes, err := json.Marshal(data) + if err != nil { + return "", err + } + return string(bytes), nil +} + +// ToYAML serializes TraceFile to YAML string +func (obj *TraceFile) ToYAML() (string, error) { + ctx := NewSaveContext() + data := obj.Save(ctx) + bytes, err := yaml.Marshal(data) + if err != nil { + return "", err + } + return string(bytes), nil +} + +// FromJSON creates TraceFile from JSON string +func TraceFileFromJSON(jsonStr string) (TraceFile, error) { + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonStr), &data); err != nil { + return TraceFile{}, err + } + ctx := NewLoadContext() + return LoadTraceFile(data, ctx) +} + +// FromYAML creates TraceFile from YAML string +func TraceFileFromYAML(yamlStr string) (TraceFile, error) { + var data map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlStr), &data); err != nil { + return TraceFile{}, err + } + ctx := NewLoadContext() + return LoadTraceFile(data, ctx) +} diff --git a/runtime/go/prompty/model/trace_file_test.go b/runtime/go/prompty/model/trace_file_test.go new file mode 100644 index 00000000..71b18e98 --- /dev/null +++ b/runtime/go/prompty/model/trace_file_test.go @@ -0,0 +1,154 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +package prompty_test + +import ( + "encoding/json" + "testing" + + "gopkg.in/yaml.v3" + + "prompty/model" +) + +// TestTraceFileLoadJSON tests loading TraceFile from JSON +func TestTraceFileLoadJSON(t *testing.T) { + jsonData := ` +{ + "runtime": "python", + "version": "2.0.0" +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadTraceFile(data, ctx) + if err != nil { + t.Fatalf("Failed to load TraceFile: %v", err) + } + if instance.Runtime != "python" { + t.Errorf(`Expected Runtime to be "python", got %v`, instance.Runtime) + } + if instance.Version != "2.0.0" { + t.Errorf(`Expected Version to be "2.0.0", got %v`, instance.Version) + } +} + +// TestTraceFileLoadYAML tests loading TraceFile from YAML +func TestTraceFileLoadYAML(t *testing.T) { + yamlData := ` +runtime: python +version: 2.0.0 + +` + var data map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlData), &data); err != nil { + t.Fatalf("Failed to parse YAML: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadTraceFile(data, ctx) + if err != nil { + t.Fatalf("Failed to load TraceFile: %v", err) + } + if instance.Runtime != "python" { + t.Errorf(`Expected Runtime to be "python", got %v`, instance.Runtime) + } + if instance.Version != "2.0.0" { + t.Errorf(`Expected Version to be "2.0.0", got %v`, instance.Version) + } +} + +// TestTraceFileRoundtrip tests load -> save -> load produces equivalent data +func TestTraceFileRoundtrip(t *testing.T) { + jsonData := ` +{ + "runtime": "python", + "version": "2.0.0" +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + loadCtx := prompty.NewLoadContext() + instance, err := prompty.LoadTraceFile(data, loadCtx) + if err != nil { + t.Fatalf("Failed to load TraceFile: %v", err) + } + saveCtx := prompty.NewSaveContext() + savedData := instance.Save(saveCtx) + + reloaded, err := prompty.LoadTraceFile(savedData, loadCtx) + if err != nil { + t.Fatalf("Failed to reload TraceFile: %v", err) + } + if reloaded.Runtime != "python" { + t.Errorf(`Expected Runtime to be "python", got %v`, reloaded.Runtime) + } + if reloaded.Version != "2.0.0" { + t.Errorf(`Expected Version to be "2.0.0", got %v`, reloaded.Version) + } +} + +// TestTraceFileToJSON tests that ToJSON produces valid JSON +func TestTraceFileToJSON(t *testing.T) { + jsonData := ` +{ + "runtime": "python", + "version": "2.0.0" +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadTraceFile(data, ctx) + if err != nil { + t.Fatalf("Failed to load TraceFile: %v", err) + } + jsonOutput, err := instance.ToJSON() + if err != nil { + t.Fatalf("Failed to convert to JSON: %v", err) + } + + var parsed map[string]interface{} + if err := json.Unmarshal([]byte(jsonOutput), &parsed); err != nil { + t.Fatalf("Failed to parse generated JSON: %v", err) + } +} + +// TestTraceFileToYAML tests that ToYAML produces valid YAML +func TestTraceFileToYAML(t *testing.T) { + jsonData := ` +{ + "runtime": "python", + "version": "2.0.0" +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadTraceFile(data, ctx) + if err != nil { + t.Fatalf("Failed to load TraceFile: %v", err) + } + yamlOutput, err := instance.ToYAML() + if err != nil { + t.Fatalf("Failed to convert to YAML: %v", err) + } + + var parsed map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlOutput), &parsed); err != nil { + t.Fatalf("Failed to parse generated YAML: %v", err) + } +} diff --git a/runtime/go/prompty/model/trace_span.go b/runtime/go/prompty/model/trace_span.go new file mode 100644 index 00000000..54bb1768 --- /dev/null +++ b/runtime/go/prompty/model/trace_span.go @@ -0,0 +1,149 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. +// Group: tracing + +package prompty + +import ( + "encoding/json" + + "gopkg.in/yaml.v3" +) + +// TraceSpan represents A single trace span capturing one pipeline stage or function invocation. +// Spans nest via the `__frames` field to form a tree representing the +// full execution (§3.6.1). + +type TraceSpan struct { + Name string `json:"name" yaml:"name"` + _Time TraceTime `json:"__time" yaml:"__time"` + Signature *string `json:"signature,omitempty" yaml:"signature,omitempty"` + Inputs map[string]interface{} `json:"inputs,omitempty" yaml:"inputs,omitempty"` + Output *interface{} `json:"output,omitempty" yaml:"output,omitempty"` + Error *string `json:"error,omitempty" yaml:"error,omitempty"` + _Usage *TokenUsage `json:"__usage,omitempty" yaml:"__usage,omitempty"` + Attributes map[string]interface{} `json:"attributes,omitempty" yaml:"attributes,omitempty"` + _Frames []interface{} `json:"__frames,omitempty" yaml:"__frames,omitempty"` +} + +// LoadTraceSpan creates a TraceSpan from a map[string]interface{} +func LoadTraceSpan(data interface{}, ctx *LoadContext) (TraceSpan, error) { + result := TraceSpan{} + + // Load from map + if m, ok := data.(map[string]interface{}); ok { + if val, ok := m["name"]; ok && val != nil { + result.Name = string(val.(string)) + } + if val, ok := m["__time"]; ok && val != nil { + if m, ok := val.(map[string]interface{}); ok { + loaded, _ := LoadTraceTime(m, ctx) + result._Time = loaded + } + } + if val, ok := m["signature"]; ok && val != nil { + v := string(val.(string)) + result.Signature = &v + } + if val, ok := m["inputs"]; ok && val != nil { + if m, ok := val.(map[string]interface{}); ok { + result.Inputs = m + } + } + if val, ok := m["output"]; ok && val != nil { + result.Output = &val + } + if val, ok := m["error"]; ok && val != nil { + v := string(val.(string)) + result.Error = &v + } + if val, ok := m["__usage"]; ok && val != nil { + if m, ok := val.(map[string]interface{}); ok { + loaded, _ := LoadTokenUsage(m, ctx) + result._Usage = &loaded + } + } + if val, ok := m["attributes"]; ok && val != nil { + if m, ok := val.(map[string]interface{}); ok { + result.Attributes = m + } + } + if val, ok := m["__frames"]; ok && val != nil { + if arr, ok := val.([]interface{}); ok { + result._Frames = arr + } + } + } + + return result, nil +} + +// Save serializes TraceSpan to map[string]interface{} +func (obj *TraceSpan) Save(ctx *SaveContext) map[string]interface{} { + result := make(map[string]interface{}) + result["name"] = obj.Name + + result["__time"] = obj._Time.Save(ctx) + if obj.Signature != nil { + result["signature"] = *obj.Signature + } + if obj.Inputs != nil { + result["inputs"] = obj.Inputs + } + if obj.Output != nil { + result["output"] = *obj.Output + } + if obj.Error != nil { + result["error"] = *obj.Error + } + if obj._Usage != nil { + result["__usage"] = obj._Usage.Save(ctx) + } + if obj.Attributes != nil { + result["attributes"] = obj.Attributes + } + result["__frames"] = obj._Frames + + return result +} + +// ToJSON serializes TraceSpan to JSON string +func (obj *TraceSpan) ToJSON() (string, error) { + ctx := NewSaveContext() + data := obj.Save(ctx) + bytes, err := json.Marshal(data) + if err != nil { + return "", err + } + return string(bytes), nil +} + +// ToYAML serializes TraceSpan to YAML string +func (obj *TraceSpan) ToYAML() (string, error) { + ctx := NewSaveContext() + data := obj.Save(ctx) + bytes, err := yaml.Marshal(data) + if err != nil { + return "", err + } + return string(bytes), nil +} + +// FromJSON creates TraceSpan from JSON string +func TraceSpanFromJSON(jsonStr string) (TraceSpan, error) { + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonStr), &data); err != nil { + return TraceSpan{}, err + } + ctx := NewLoadContext() + return LoadTraceSpan(data, ctx) +} + +// FromYAML creates TraceSpan from YAML string +func TraceSpanFromYAML(yamlStr string) (TraceSpan, error) { + var data map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlStr), &data); err != nil { + return TraceSpan{}, err + } + ctx := NewLoadContext() + return LoadTraceSpan(data, ctx) +} diff --git a/runtime/go/prompty/model/trace_span_test.go b/runtime/go/prompty/model/trace_span_test.go new file mode 100644 index 00000000..20cc0876 --- /dev/null +++ b/runtime/go/prompty/model/trace_span_test.go @@ -0,0 +1,168 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +package prompty_test + +import ( + "encoding/json" + "testing" + + "gopkg.in/yaml.v3" + + "prompty/model" +) + +// TestTraceSpanLoadJSON tests loading TraceSpan from JSON +func TestTraceSpanLoadJSON(t *testing.T) { + jsonData := ` +{ + "name": "prompty.core.pipeline.run", + "signature": "prompty.core.pipeline.run", + "error": "Connection refused" +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadTraceSpan(data, ctx) + if err != nil { + t.Fatalf("Failed to load TraceSpan: %v", err) + } + if instance.Name != "prompty.core.pipeline.run" { + t.Errorf(`Expected Name to be "prompty.core.pipeline.run", got %v`, instance.Name) + } + if instance.Signature == nil || *instance.Signature != "prompty.core.pipeline.run" { + t.Errorf(`Expected Signature to be "prompty.core.pipeline.run", got %v`, instance.Signature) + } + if instance.Error == nil || *instance.Error != "Connection refused" { + t.Errorf(`Expected Error to be "Connection refused", got %v`, instance.Error) + } +} + +// TestTraceSpanLoadYAML tests loading TraceSpan from YAML +func TestTraceSpanLoadYAML(t *testing.T) { + yamlData := ` +name: prompty.core.pipeline.run +signature: prompty.core.pipeline.run +error: Connection refused + +` + var data map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlData), &data); err != nil { + t.Fatalf("Failed to parse YAML: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadTraceSpan(data, ctx) + if err != nil { + t.Fatalf("Failed to load TraceSpan: %v", err) + } + if instance.Name != "prompty.core.pipeline.run" { + t.Errorf(`Expected Name to be "prompty.core.pipeline.run", got %v`, instance.Name) + } + if instance.Signature == nil || *instance.Signature != "prompty.core.pipeline.run" { + t.Errorf(`Expected Signature to be "prompty.core.pipeline.run", got %v`, instance.Signature) + } + if instance.Error == nil || *instance.Error != "Connection refused" { + t.Errorf(`Expected Error to be "Connection refused", got %v`, instance.Error) + } +} + +// TestTraceSpanRoundtrip tests load -> save -> load produces equivalent data +func TestTraceSpanRoundtrip(t *testing.T) { + jsonData := ` +{ + "name": "prompty.core.pipeline.run", + "signature": "prompty.core.pipeline.run", + "error": "Connection refused" +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + loadCtx := prompty.NewLoadContext() + instance, err := prompty.LoadTraceSpan(data, loadCtx) + if err != nil { + t.Fatalf("Failed to load TraceSpan: %v", err) + } + saveCtx := prompty.NewSaveContext() + savedData := instance.Save(saveCtx) + + reloaded, err := prompty.LoadTraceSpan(savedData, loadCtx) + if err != nil { + t.Fatalf("Failed to reload TraceSpan: %v", err) + } + if reloaded.Name != "prompty.core.pipeline.run" { + t.Errorf(`Expected Name to be "prompty.core.pipeline.run", got %v`, reloaded.Name) + } + if reloaded.Signature == nil || *reloaded.Signature != "prompty.core.pipeline.run" { + t.Errorf(`Expected Signature to be "prompty.core.pipeline.run", got %v`, reloaded.Signature) + } + if reloaded.Error == nil || *reloaded.Error != "Connection refused" { + t.Errorf(`Expected Error to be "Connection refused", got %v`, reloaded.Error) + } +} + +// TestTraceSpanToJSON tests that ToJSON produces valid JSON +func TestTraceSpanToJSON(t *testing.T) { + jsonData := ` +{ + "name": "prompty.core.pipeline.run", + "signature": "prompty.core.pipeline.run", + "error": "Connection refused" +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadTraceSpan(data, ctx) + if err != nil { + t.Fatalf("Failed to load TraceSpan: %v", err) + } + jsonOutput, err := instance.ToJSON() + if err != nil { + t.Fatalf("Failed to convert to JSON: %v", err) + } + + var parsed map[string]interface{} + if err := json.Unmarshal([]byte(jsonOutput), &parsed); err != nil { + t.Fatalf("Failed to parse generated JSON: %v", err) + } +} + +// TestTraceSpanToYAML tests that ToYAML produces valid YAML +func TestTraceSpanToYAML(t *testing.T) { + jsonData := ` +{ + "name": "prompty.core.pipeline.run", + "signature": "prompty.core.pipeline.run", + "error": "Connection refused" +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadTraceSpan(data, ctx) + if err != nil { + t.Fatalf("Failed to load TraceSpan: %v", err) + } + yamlOutput, err := instance.ToYAML() + if err != nil { + t.Fatalf("Failed to convert to YAML: %v", err) + } + + var parsed map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlOutput), &parsed); err != nil { + t.Fatalf("Failed to parse generated YAML: %v", err) + } +} diff --git a/runtime/go/prompty/model/trace_time.go b/runtime/go/prompty/model/trace_time.go new file mode 100644 index 00000000..cc0afc8f --- /dev/null +++ b/runtime/go/prompty/model/trace_time.go @@ -0,0 +1,103 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. +// Group: tracing + +package prompty + +import ( + "encoding/json" + + "gopkg.in/yaml.v3" +) + +// TraceTime represents Timing information for a trace span. + +type TraceTime struct { + Start string `json:"start" yaml:"start"` + End string `json:"end" yaml:"end"` + Duration float64 `json:"duration" yaml:"duration"` +} + +// LoadTraceTime creates a TraceTime from a map[string]interface{} +func LoadTraceTime(data interface{}, ctx *LoadContext) (TraceTime, error) { + result := TraceTime{} + + // Load from map + if m, ok := data.(map[string]interface{}); ok { + if val, ok := m["start"]; ok && val != nil { + result.Start = string(val.(string)) + } + if val, ok := m["end"]; ok && val != nil { + result.End = string(val.(string)) + } + if val, ok := m["duration"]; ok && val != nil { // Handle various numeric types from JSON/YAML/roundtrip + var v float64 + switch n := val.(type) { + case int: + v = float64(n) + case int32: + v = float64(n) + case int64: + v = float64(n) + case float32: + v = float64(n) + case float64: + v = n + } + result.Duration = v + } + } + + return result, nil +} + +// Save serializes TraceTime to map[string]interface{} +func (obj *TraceTime) Save(ctx *SaveContext) map[string]interface{} { + result := make(map[string]interface{}) + result["start"] = obj.Start + result["end"] = obj.End + result["duration"] = obj.Duration + + return result +} + +// ToJSON serializes TraceTime to JSON string +func (obj *TraceTime) ToJSON() (string, error) { + ctx := NewSaveContext() + data := obj.Save(ctx) + bytes, err := json.Marshal(data) + if err != nil { + return "", err + } + return string(bytes), nil +} + +// ToYAML serializes TraceTime to YAML string +func (obj *TraceTime) ToYAML() (string, error) { + ctx := NewSaveContext() + data := obj.Save(ctx) + bytes, err := yaml.Marshal(data) + if err != nil { + return "", err + } + return string(bytes), nil +} + +// FromJSON creates TraceTime from JSON string +func TraceTimeFromJSON(jsonStr string) (TraceTime, error) { + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonStr), &data); err != nil { + return TraceTime{}, err + } + ctx := NewLoadContext() + return LoadTraceTime(data, ctx) +} + +// FromYAML creates TraceTime from YAML string +func TraceTimeFromYAML(yamlStr string) (TraceTime, error) { + var data map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlStr), &data); err != nil { + return TraceTime{}, err + } + ctx := NewLoadContext() + return LoadTraceTime(data, ctx) +} diff --git a/runtime/go/prompty/model/trace_time_test.go b/runtime/go/prompty/model/trace_time_test.go new file mode 100644 index 00000000..aab3348d --- /dev/null +++ b/runtime/go/prompty/model/trace_time_test.go @@ -0,0 +1,168 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +package prompty_test + +import ( + "encoding/json" + "testing" + + "gopkg.in/yaml.v3" + + "prompty/model" +) + +// TestTraceTimeLoadJSON tests loading TraceTime from JSON +func TestTraceTimeLoadJSON(t *testing.T) { + jsonData := ` +{ + "start": "2026-04-04T12:00:00Z", + "end": "2026-04-04T12:00:01Z", + "duration": 1000 +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadTraceTime(data, ctx) + if err != nil { + t.Fatalf("Failed to load TraceTime: %v", err) + } + if instance.Start != "2026-04-04T12:00:00Z" { + t.Errorf(`Expected Start to be "2026-04-04T12:00:00Z", got %v`, instance.Start) + } + if instance.End != "2026-04-04T12:00:01Z" { + t.Errorf(`Expected End to be "2026-04-04T12:00:01Z", got %v`, instance.End) + } + if instance.Duration != 1000 { + t.Errorf(`Expected Duration to be 1000, got %v`, instance.Duration) + } +} + +// TestTraceTimeLoadYAML tests loading TraceTime from YAML +func TestTraceTimeLoadYAML(t *testing.T) { + yamlData := ` +start: "2026-04-04T12:00:00Z" +end: "2026-04-04T12:00:01Z" +duration: 1000 + +` + var data map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlData), &data); err != nil { + t.Fatalf("Failed to parse YAML: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadTraceTime(data, ctx) + if err != nil { + t.Fatalf("Failed to load TraceTime: %v", err) + } + if instance.Start != "2026-04-04T12:00:00Z" { + t.Errorf(`Expected Start to be "2026-04-04T12:00:00Z", got %v`, instance.Start) + } + if instance.End != "2026-04-04T12:00:01Z" { + t.Errorf(`Expected End to be "2026-04-04T12:00:01Z", got %v`, instance.End) + } + if instance.Duration != 1000 { + t.Errorf(`Expected Duration to be 1000, got %v`, instance.Duration) + } +} + +// TestTraceTimeRoundtrip tests load -> save -> load produces equivalent data +func TestTraceTimeRoundtrip(t *testing.T) { + jsonData := ` +{ + "start": "2026-04-04T12:00:00Z", + "end": "2026-04-04T12:00:01Z", + "duration": 1000 +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + loadCtx := prompty.NewLoadContext() + instance, err := prompty.LoadTraceTime(data, loadCtx) + if err != nil { + t.Fatalf("Failed to load TraceTime: %v", err) + } + saveCtx := prompty.NewSaveContext() + savedData := instance.Save(saveCtx) + + reloaded, err := prompty.LoadTraceTime(savedData, loadCtx) + if err != nil { + t.Fatalf("Failed to reload TraceTime: %v", err) + } + if reloaded.Start != "2026-04-04T12:00:00Z" { + t.Errorf(`Expected Start to be "2026-04-04T12:00:00Z", got %v`, reloaded.Start) + } + if reloaded.End != "2026-04-04T12:00:01Z" { + t.Errorf(`Expected End to be "2026-04-04T12:00:01Z", got %v`, reloaded.End) + } + if reloaded.Duration != 1000 { + t.Errorf(`Expected Duration to be 1000, got %v`, reloaded.Duration) + } +} + +// TestTraceTimeToJSON tests that ToJSON produces valid JSON +func TestTraceTimeToJSON(t *testing.T) { + jsonData := ` +{ + "start": "2026-04-04T12:00:00Z", + "end": "2026-04-04T12:00:01Z", + "duration": 1000 +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadTraceTime(data, ctx) + if err != nil { + t.Fatalf("Failed to load TraceTime: %v", err) + } + jsonOutput, err := instance.ToJSON() + if err != nil { + t.Fatalf("Failed to convert to JSON: %v", err) + } + + var parsed map[string]interface{} + if err := json.Unmarshal([]byte(jsonOutput), &parsed); err != nil { + t.Fatalf("Failed to parse generated JSON: %v", err) + } +} + +// TestTraceTimeToYAML tests that ToYAML produces valid YAML +func TestTraceTimeToYAML(t *testing.T) { + jsonData := ` +{ + "start": "2026-04-04T12:00:00Z", + "end": "2026-04-04T12:00:01Z", + "duration": 1000 +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadTraceTime(data, ctx) + if err != nil { + t.Fatalf("Failed to load TraceTime: %v", err) + } + yamlOutput, err := instance.ToYAML() + if err != nil { + t.Fatalf("Failed to convert to YAML: %v", err) + } + + var parsed map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlOutput), &parsed); err != nil { + t.Fatalf("Failed to parse generated YAML: %v", err) + } +} diff --git a/runtime/go/prompty/model/validation_error.go b/runtime/go/prompty/model/validation_error.go new file mode 100644 index 00000000..c8abc347 --- /dev/null +++ b/runtime/go/prompty/model/validation_error.go @@ -0,0 +1,91 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. +// Group: core + +package prompty + +import ( + "encoding/json" + + "gopkg.in/yaml.v3" +) + +// ValidationError represents Raised when input validation fails. Each ValidationError describes a +// single property that did not satisfy its constraint. + +type ValidationError struct { + Message string `json:"message" yaml:"message"` + Property string `json:"property" yaml:"property"` + Constraint string `json:"constraint" yaml:"constraint"` +} + +// LoadValidationError creates a ValidationError from a map[string]interface{} +func LoadValidationError(data interface{}, ctx *LoadContext) (ValidationError, error) { + result := ValidationError{} + + // Load from map + if m, ok := data.(map[string]interface{}); ok { + if val, ok := m["message"]; ok && val != nil { + result.Message = string(val.(string)) + } + if val, ok := m["property"]; ok && val != nil { + result.Property = string(val.(string)) + } + if val, ok := m["constraint"]; ok && val != nil { + result.Constraint = string(val.(string)) + } + } + + return result, nil +} + +// Save serializes ValidationError to map[string]interface{} +func (obj *ValidationError) Save(ctx *SaveContext) map[string]interface{} { + result := make(map[string]interface{}) + result["message"] = obj.Message + result["property"] = obj.Property + result["constraint"] = obj.Constraint + + return result +} + +// ToJSON serializes ValidationError to JSON string +func (obj *ValidationError) ToJSON() (string, error) { + ctx := NewSaveContext() + data := obj.Save(ctx) + bytes, err := json.Marshal(data) + if err != nil { + return "", err + } + return string(bytes), nil +} + +// ToYAML serializes ValidationError to YAML string +func (obj *ValidationError) ToYAML() (string, error) { + ctx := NewSaveContext() + data := obj.Save(ctx) + bytes, err := yaml.Marshal(data) + if err != nil { + return "", err + } + return string(bytes), nil +} + +// FromJSON creates ValidationError from JSON string +func ValidationErrorFromJSON(jsonStr string) (ValidationError, error) { + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonStr), &data); err != nil { + return ValidationError{}, err + } + ctx := NewLoadContext() + return LoadValidationError(data, ctx) +} + +// FromYAML creates ValidationError from YAML string +func ValidationErrorFromYAML(yamlStr string) (ValidationError, error) { + var data map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlStr), &data); err != nil { + return ValidationError{}, err + } + ctx := NewLoadContext() + return LoadValidationError(data, ctx) +} diff --git a/runtime/go/prompty/model/validation_error_test.go b/runtime/go/prompty/model/validation_error_test.go new file mode 100644 index 00000000..97cc0f01 --- /dev/null +++ b/runtime/go/prompty/model/validation_error_test.go @@ -0,0 +1,168 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +package prompty_test + +import ( + "encoding/json" + "testing" + + "gopkg.in/yaml.v3" + + "prompty/model" +) + +// TestValidationErrorLoadJSON tests loading ValidationError from JSON +func TestValidationErrorLoadJSON(t *testing.T) { + jsonData := ` +{ + "message": "Missing required input: firstName", + "property": "firstName", + "constraint": "required" +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadValidationError(data, ctx) + if err != nil { + t.Fatalf("Failed to load ValidationError: %v", err) + } + if instance.Message != "Missing required input: firstName" { + t.Errorf(`Expected Message to be "Missing required input: firstName", got %v`, instance.Message) + } + if instance.Property != "firstName" { + t.Errorf(`Expected Property to be "firstName", got %v`, instance.Property) + } + if instance.Constraint != "required" { + t.Errorf(`Expected Constraint to be "required", got %v`, instance.Constraint) + } +} + +// TestValidationErrorLoadYAML tests loading ValidationError from YAML +func TestValidationErrorLoadYAML(t *testing.T) { + yamlData := ` +message: "Missing required input: firstName" +property: firstName +constraint: required + +` + var data map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlData), &data); err != nil { + t.Fatalf("Failed to parse YAML: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadValidationError(data, ctx) + if err != nil { + t.Fatalf("Failed to load ValidationError: %v", err) + } + if instance.Message != "Missing required input: firstName" { + t.Errorf(`Expected Message to be "Missing required input: firstName", got %v`, instance.Message) + } + if instance.Property != "firstName" { + t.Errorf(`Expected Property to be "firstName", got %v`, instance.Property) + } + if instance.Constraint != "required" { + t.Errorf(`Expected Constraint to be "required", got %v`, instance.Constraint) + } +} + +// TestValidationErrorRoundtrip tests load -> save -> load produces equivalent data +func TestValidationErrorRoundtrip(t *testing.T) { + jsonData := ` +{ + "message": "Missing required input: firstName", + "property": "firstName", + "constraint": "required" +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + loadCtx := prompty.NewLoadContext() + instance, err := prompty.LoadValidationError(data, loadCtx) + if err != nil { + t.Fatalf("Failed to load ValidationError: %v", err) + } + saveCtx := prompty.NewSaveContext() + savedData := instance.Save(saveCtx) + + reloaded, err := prompty.LoadValidationError(savedData, loadCtx) + if err != nil { + t.Fatalf("Failed to reload ValidationError: %v", err) + } + if reloaded.Message != "Missing required input: firstName" { + t.Errorf(`Expected Message to be "Missing required input: firstName", got %v`, reloaded.Message) + } + if reloaded.Property != "firstName" { + t.Errorf(`Expected Property to be "firstName", got %v`, reloaded.Property) + } + if reloaded.Constraint != "required" { + t.Errorf(`Expected Constraint to be "required", got %v`, reloaded.Constraint) + } +} + +// TestValidationErrorToJSON tests that ToJSON produces valid JSON +func TestValidationErrorToJSON(t *testing.T) { + jsonData := ` +{ + "message": "Missing required input: firstName", + "property": "firstName", + "constraint": "required" +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadValidationError(data, ctx) + if err != nil { + t.Fatalf("Failed to load ValidationError: %v", err) + } + jsonOutput, err := instance.ToJSON() + if err != nil { + t.Fatalf("Failed to convert to JSON: %v", err) + } + + var parsed map[string]interface{} + if err := json.Unmarshal([]byte(jsonOutput), &parsed); err != nil { + t.Fatalf("Failed to parse generated JSON: %v", err) + } +} + +// TestValidationErrorToYAML tests that ToYAML produces valid YAML +func TestValidationErrorToYAML(t *testing.T) { + jsonData := ` +{ + "message": "Missing required input: firstName", + "property": "firstName", + "constraint": "required" +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadValidationError(data, ctx) + if err != nil { + t.Fatalf("Failed to load ValidationError: %v", err) + } + yamlOutput, err := instance.ToYAML() + if err != nil { + t.Fatalf("Failed to convert to YAML: %v", err) + } + + var parsed map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlOutput), &parsed); err != nil { + t.Fatalf("Failed to parse generated YAML: %v", err) + } +} diff --git a/runtime/go/prompty/model/validation_result.go b/runtime/go/prompty/model/validation_result.go new file mode 100644 index 00000000..d5bd6436 --- /dev/null +++ b/runtime/go/prompty/model/validation_result.go @@ -0,0 +1,101 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. +// Group: core + +package prompty + +import ( + "encoding/json" + + "gopkg.in/yaml.v3" +) + +// ValidationResult represents The result of validating inputs against an agent's inputSchema. +// Returned by `validate_inputs` (§12.2) to indicate whether all +// required inputs are present and satisfy their constraints. + +type ValidationResult struct { + Valid bool `json:"valid" yaml:"valid"` + Errors []ValidationError `json:"errors" yaml:"errors"` +} + +// LoadValidationResult creates a ValidationResult from a map[string]interface{} +func LoadValidationResult(data interface{}, ctx *LoadContext) (ValidationResult, error) { + result := ValidationResult{} + + // Load from map + if m, ok := data.(map[string]interface{}); ok { + if val, ok := m["valid"]; ok && val != nil { + result.Valid = val.(bool) + } + if val, ok := m["errors"]; ok && val != nil { + if arr, ok := val.([]interface{}); ok { + result.Errors = make([]ValidationError, len(arr)) + for i, v := range arr { + if item, ok := v.(map[string]interface{}); ok { + loaded, _ := LoadValidationError(item, ctx) + result.Errors[i] = loaded + } + } + } + } + } + + return result, nil +} + +// Save serializes ValidationResult to map[string]interface{} +func (obj *ValidationResult) Save(ctx *SaveContext) map[string]interface{} { + result := make(map[string]interface{}) + result["valid"] = obj.Valid + if obj.Errors != nil { + arr := make([]interface{}, len(obj.Errors)) + for i, item := range obj.Errors { + arr[i] = item.Save(ctx) + } + result["errors"] = arr + } + + return result +} + +// ToJSON serializes ValidationResult to JSON string +func (obj *ValidationResult) ToJSON() (string, error) { + ctx := NewSaveContext() + data := obj.Save(ctx) + bytes, err := json.Marshal(data) + if err != nil { + return "", err + } + return string(bytes), nil +} + +// ToYAML serializes ValidationResult to YAML string +func (obj *ValidationResult) ToYAML() (string, error) { + ctx := NewSaveContext() + data := obj.Save(ctx) + bytes, err := yaml.Marshal(data) + if err != nil { + return "", err + } + return string(bytes), nil +} + +// FromJSON creates ValidationResult from JSON string +func ValidationResultFromJSON(jsonStr string) (ValidationResult, error) { + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonStr), &data); err != nil { + return ValidationResult{}, err + } + ctx := NewLoadContext() + return LoadValidationResult(data, ctx) +} + +// FromYAML creates ValidationResult from YAML string +func ValidationResultFromYAML(yamlStr string) (ValidationResult, error) { + var data map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlStr), &data); err != nil { + return ValidationResult{}, err + } + ctx := NewLoadContext() + return LoadValidationResult(data, ctx) +} diff --git a/runtime/go/prompty/model/validation_result_test.go b/runtime/go/prompty/model/validation_result_test.go new file mode 100644 index 00000000..addb53e0 --- /dev/null +++ b/runtime/go/prompty/model/validation_result_test.go @@ -0,0 +1,145 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +package prompty_test + +import ( + "encoding/json" + "testing" + + "gopkg.in/yaml.v3" + + "prompty/model" +) + +// TestValidationResultLoadJSON tests loading ValidationResult from JSON +func TestValidationResultLoadJSON(t *testing.T) { + jsonData := ` +{ + "valid": true, + "errors": [] +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadValidationResult(data, ctx) + if err != nil { + t.Fatalf("Failed to load ValidationResult: %v", err) + } + if instance.Valid != true { + t.Errorf(`Expected Valid to be true, got %v`, instance.Valid) + } +} + +// TestValidationResultLoadYAML tests loading ValidationResult from YAML +func TestValidationResultLoadYAML(t *testing.T) { + yamlData := ` +valid: true +errors: [] + +` + var data map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlData), &data); err != nil { + t.Fatalf("Failed to parse YAML: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadValidationResult(data, ctx) + if err != nil { + t.Fatalf("Failed to load ValidationResult: %v", err) + } + if instance.Valid != true { + t.Errorf(`Expected Valid to be true, got %v`, instance.Valid) + } +} + +// TestValidationResultRoundtrip tests load -> save -> load produces equivalent data +func TestValidationResultRoundtrip(t *testing.T) { + jsonData := ` +{ + "valid": true, + "errors": [] +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + loadCtx := prompty.NewLoadContext() + instance, err := prompty.LoadValidationResult(data, loadCtx) + if err != nil { + t.Fatalf("Failed to load ValidationResult: %v", err) + } + saveCtx := prompty.NewSaveContext() + savedData := instance.Save(saveCtx) + + reloaded, err := prompty.LoadValidationResult(savedData, loadCtx) + if err != nil { + t.Fatalf("Failed to reload ValidationResult: %v", err) + } + if reloaded.Valid != true { + t.Errorf(`Expected Valid to be true, got %v`, reloaded.Valid) + } +} + +// TestValidationResultToJSON tests that ToJSON produces valid JSON +func TestValidationResultToJSON(t *testing.T) { + jsonData := ` +{ + "valid": true, + "errors": [] +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadValidationResult(data, ctx) + if err != nil { + t.Fatalf("Failed to load ValidationResult: %v", err) + } + jsonOutput, err := instance.ToJSON() + if err != nil { + t.Fatalf("Failed to convert to JSON: %v", err) + } + + var parsed map[string]interface{} + if err := json.Unmarshal([]byte(jsonOutput), &parsed); err != nil { + t.Fatalf("Failed to parse generated JSON: %v", err) + } +} + +// TestValidationResultToYAML tests that ToYAML produces valid YAML +func TestValidationResultToYAML(t *testing.T) { + jsonData := ` +{ + "valid": true, + "errors": [] +} +` + var data map[string]interface{} + if err := json.Unmarshal([]byte(jsonData), &data); err != nil { + t.Fatalf("Failed to parse JSON: %v", err) + } + + ctx := prompty.NewLoadContext() + instance, err := prompty.LoadValidationResult(data, ctx) + if err != nil { + t.Fatalf("Failed to load ValidationResult: %v", err) + } + yamlOutput, err := instance.ToYAML() + if err != nil { + t.Fatalf("Failed to convert to YAML: %v", err) + } + + var parsed map[string]interface{} + if err := yaml.Unmarshal([]byte(yamlOutput), &parsed); err != nil { + t.Fatalf("Failed to parse generated YAML: %v", err) + } +} diff --git a/runtime/python/prompty/prompty/model/__init__.py b/runtime/python/prompty/prompty/model/__init__.py index 61aeb41c..a1b420d5 100644 --- a/runtime/python/prompty/prompty/model/__init__.py +++ b/runtime/python/prompty/prompty/model/__init__.py @@ -32,8 +32,12 @@ ) from .core import ( ArrayProperty, + FileNotFoundError, + InvokerError, ObjectProperty, Property, + ValidationError, + ValidationResult, ) from .events import ( CompactionCompletePayload, @@ -66,6 +70,9 @@ Renderer, TurnOptions, ) +from .streaming import ( + StreamOptions, +) from .template import ( FormatConfig, ParserConfig, @@ -83,6 +90,23 @@ ToolContext, ToolDispatchResult, ) +from .tracing import ( + TraceFile, + TraceSpan, + TraceTime, +) +from .wire import ( + AnthropicImageBlock, + AnthropicImageSource, + AnthropicMessagesRequest, + AnthropicMessagesResponse, + AnthropicTextBlock, + AnthropicToolDefinition, + AnthropicToolResultBlock, + AnthropicToolUseBlock, + AnthropicUsage, + AnthropicWireMessage, +) __all__ = [ "LoadContext", @@ -90,6 +114,10 @@ "Property", "ObjectProperty", "ArrayProperty", + "FileNotFoundError", + "InvokerError", + "ValidationError", + "ValidationResult", "Connection", "ReferenceConnection", "RemoteConnection", @@ -148,4 +176,18 @@ "ThinkingChunk", "ToolChunk", "ErrorChunk", + "AnthropicImageBlock", + "AnthropicImageSource", + "AnthropicMessagesRequest", + "AnthropicMessagesResponse", + "AnthropicTextBlock", + "AnthropicToolDefinition", + "AnthropicToolResultBlock", + "AnthropicToolUseBlock", + "AnthropicUsage", + "AnthropicWireMessage", + "StreamOptions", + "TraceFile", + "TraceSpan", + "TraceTime", ] diff --git a/runtime/python/prompty/prompty/model/core/_FileNotFoundError.py b/runtime/python/prompty/prompty/model/core/_FileNotFoundError.py new file mode 100644 index 00000000..c663be66 --- /dev/null +++ b/runtime/python/prompty/prompty/model/core/_FileNotFoundError.py @@ -0,0 +1,105 @@ +########################################## +# WARNING: This is an auto-generated file. +# DO NOT EDIT THIS FILE DIRECTLY +# ANY EDITS WILL BE LOST +########################################## + +from dataclasses import dataclass, field +from typing import Any, ClassVar + +from .._context import LoadContext, SaveContext + + +@dataclass +class FileNotFoundError: + """Raised when a referenced file cannot be found. This applies to both + .prompty files and ${file:path} references in frontmatter. + + Attributes + ---------- + message : str + Human-readable error message + path : str + The file path that could not be resolved + """ + + _shorthand_property: ClassVar[str | None] = None + + message: str = field(default="") + path: str = field(default="") + + @staticmethod + def load(data: Any, context: LoadContext | None = None) -> "FileNotFoundError": + """Load a FileNotFoundError instance. + Args: + data (Any): The data to load the instance from. + context (Optional[LoadContext]): Optional context with pre/post processing callbacks. + Returns: + FileNotFoundError: The loaded FileNotFoundError instance. + + """ + + if context is not None: + data = context.process_input(data) + + if not isinstance(data, dict): + raise ValueError(f"Invalid data for FileNotFoundError: {data}") + + # create new instance + instance = FileNotFoundError() + + if data is not None and "message" in data: + instance.message = data["message"] + if data is not None and "path" in data: + instance.path = data["path"] + if context is not None: + instance = context.process_output(instance) + return instance + + def save(self, context: SaveContext | None = None) -> dict[str, Any]: + """Save the FileNotFoundError instance to a dictionary. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + Returns: + dict[str, Any]: The dictionary representation of this instance. + + """ + obj = self + if context is not None: + obj = context.process_object(obj) + + result: dict[str, Any] = {} + + if obj.message is not None: + result["message"] = obj.message + if obj.path is not None: + result["path"] = obj.path + + if context is not None: + result = context.process_dict(result) + return result + + def to_yaml(self, context: SaveContext | None = None) -> str: + """Convert the FileNotFoundError instance to a YAML string. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + Returns: + str: The YAML string representation of this instance. + + """ + if context is None: + context = SaveContext() + return context.to_yaml(self.save(context)) + + def to_json(self, context: SaveContext | None = None, indent: int = 2) -> str: + """Convert the FileNotFoundError instance to a JSON string. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + indent (int): Number of spaces for indentation. Defaults to 2. + Returns: + str: The JSON string representation of this instance. + + """ + if context is None: + context = SaveContext() + return context.to_json(self.save(context), indent) diff --git a/runtime/python/prompty/prompty/model/core/_InvokerError.py b/runtime/python/prompty/prompty/model/core/_InvokerError.py new file mode 100644 index 00000000..4b3d89f6 --- /dev/null +++ b/runtime/python/prompty/prompty/model/core/_InvokerError.py @@ -0,0 +1,113 @@ +########################################## +# WARNING: This is an auto-generated file. +# DO NOT EDIT THIS FILE DIRECTLY +# ANY EDITS WILL BE LOST +########################################## + +from dataclasses import dataclass, field +from typing import Any, ClassVar + +from .._context import LoadContext, SaveContext + + +@dataclass +class InvokerError: + """Raised when no invoker implementation is registered for a given component + and key. For example, if no renderer is registered for the key "jinja2", + an InvokerError is raised. + + Attributes + ---------- + message : str + Human-readable error message + component : str + The pipeline component type that was being looked up (e.g., 'renderer', 'parser', 'executor', 'processor') + key : str + The registration key that was not found + """ + + _shorthand_property: ClassVar[str | None] = None + + message: str = field(default="") + component: str = field(default="") + key: str = field(default="") + + @staticmethod + def load(data: Any, context: LoadContext | None = None) -> "InvokerError": + """Load a InvokerError instance. + Args: + data (Any): The data to load the instance from. + context (Optional[LoadContext]): Optional context with pre/post processing callbacks. + Returns: + InvokerError: The loaded InvokerError instance. + + """ + + if context is not None: + data = context.process_input(data) + + if not isinstance(data, dict): + raise ValueError(f"Invalid data for InvokerError: {data}") + + # create new instance + instance = InvokerError() + + if data is not None and "message" in data: + instance.message = data["message"] + if data is not None and "component" in data: + instance.component = data["component"] + if data is not None and "key" in data: + instance.key = data["key"] + if context is not None: + instance = context.process_output(instance) + return instance + + def save(self, context: SaveContext | None = None) -> dict[str, Any]: + """Save the InvokerError instance to a dictionary. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + Returns: + dict[str, Any]: The dictionary representation of this instance. + + """ + obj = self + if context is not None: + obj = context.process_object(obj) + + result: dict[str, Any] = {} + + if obj.message is not None: + result["message"] = obj.message + if obj.component is not None: + result["component"] = obj.component + if obj.key is not None: + result["key"] = obj.key + + if context is not None: + result = context.process_dict(result) + return result + + def to_yaml(self, context: SaveContext | None = None) -> str: + """Convert the InvokerError instance to a YAML string. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + Returns: + str: The YAML string representation of this instance. + + """ + if context is None: + context = SaveContext() + return context.to_yaml(self.save(context)) + + def to_json(self, context: SaveContext | None = None, indent: int = 2) -> str: + """Convert the InvokerError instance to a JSON string. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + indent (int): Number of spaces for indentation. Defaults to 2. + Returns: + str: The JSON string representation of this instance. + + """ + if context is None: + context = SaveContext() + return context.to_json(self.save(context), indent) diff --git a/runtime/python/prompty/prompty/model/core/_ValidationError.py b/runtime/python/prompty/prompty/model/core/_ValidationError.py new file mode 100644 index 00000000..2d1ee4cc --- /dev/null +++ b/runtime/python/prompty/prompty/model/core/_ValidationError.py @@ -0,0 +1,112 @@ +########################################## +# WARNING: This is an auto-generated file. +# DO NOT EDIT THIS FILE DIRECTLY +# ANY EDITS WILL BE LOST +########################################## + +from dataclasses import dataclass, field +from typing import Any, ClassVar + +from .._context import LoadContext, SaveContext + + +@dataclass +class ValidationError: + """Raised when input validation fails. Each ValidationError describes a + single property that did not satisfy its constraint. + + Attributes + ---------- + message : str + Human-readable error message + property : str + The name of the property that failed validation + constraint : str + The constraint that was violated (e.g., 'required', 'type') + """ + + _shorthand_property: ClassVar[str | None] = None + + message: str = field(default="") + property: str = field(default="") + constraint: str = field(default="") + + @staticmethod + def load(data: Any, context: LoadContext | None = None) -> "ValidationError": + """Load a ValidationError instance. + Args: + data (Any): The data to load the instance from. + context (Optional[LoadContext]): Optional context with pre/post processing callbacks. + Returns: + ValidationError: The loaded ValidationError instance. + + """ + + if context is not None: + data = context.process_input(data) + + if not isinstance(data, dict): + raise ValueError(f"Invalid data for ValidationError: {data}") + + # create new instance + instance = ValidationError() + + if data is not None and "message" in data: + instance.message = data["message"] + if data is not None and "property" in data: + instance.property = data["property"] + if data is not None and "constraint" in data: + instance.constraint = data["constraint"] + if context is not None: + instance = context.process_output(instance) + return instance + + def save(self, context: SaveContext | None = None) -> dict[str, Any]: + """Save the ValidationError instance to a dictionary. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + Returns: + dict[str, Any]: The dictionary representation of this instance. + + """ + obj = self + if context is not None: + obj = context.process_object(obj) + + result: dict[str, Any] = {} + + if obj.message is not None: + result["message"] = obj.message + if obj.property is not None: + result["property"] = obj.property + if obj.constraint is not None: + result["constraint"] = obj.constraint + + if context is not None: + result = context.process_dict(result) + return result + + def to_yaml(self, context: SaveContext | None = None) -> str: + """Convert the ValidationError instance to a YAML string. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + Returns: + str: The YAML string representation of this instance. + + """ + if context is None: + context = SaveContext() + return context.to_yaml(self.save(context)) + + def to_json(self, context: SaveContext | None = None, indent: int = 2) -> str: + """Convert the ValidationError instance to a JSON string. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + indent (int): Number of spaces for indentation. Defaults to 2. + Returns: + str: The JSON string representation of this instance. + + """ + if context is None: + context = SaveContext() + return context.to_json(self.save(context), indent) diff --git a/runtime/python/prompty/prompty/model/core/_ValidationResult.py b/runtime/python/prompty/prompty/model/core/_ValidationResult.py new file mode 100644 index 00000000..26390d8f --- /dev/null +++ b/runtime/python/prompty/prompty/model/core/_ValidationResult.py @@ -0,0 +1,130 @@ +########################################## +# WARNING: This is an auto-generated file. +# DO NOT EDIT THIS FILE DIRECTLY +# ANY EDITS WILL BE LOST +########################################## + +from dataclasses import dataclass, field +from typing import Any, ClassVar + +from .._context import LoadContext, SaveContext +from ._ValidationError import ValidationError + + +@dataclass +class ValidationResult: + """The result of validating inputs against an agent's inputSchema. + Returned by `validate_inputs` (§12.2) to indicate whether all + required inputs are present and satisfy their constraints. + + Attributes + ---------- + valid : bool + Whether all inputs passed validation + errors : list[ValidationError] + List of validation errors (empty when valid is true) + """ + + _shorthand_property: ClassVar[str | None] = None + + valid: bool = field(default=False) + errors: list[ValidationError] = field(default_factory=list) + + @staticmethod + def load(data: Any, context: LoadContext | None = None) -> "ValidationResult": + """Load a ValidationResult instance. + Args: + data (Any): The data to load the instance from. + context (Optional[LoadContext]): Optional context with pre/post processing callbacks. + Returns: + ValidationResult: The loaded ValidationResult instance. + + """ + + if context is not None: + data = context.process_input(data) + + if not isinstance(data, dict): + raise ValueError(f"Invalid data for ValidationResult: {data}") + + # create new instance + instance = ValidationResult() + + if data is not None and "valid" in data: + instance.valid = data["valid"] + if data is not None and "errors" in data: + instance.errors = ValidationResult.load_errors(data["errors"], context) + if context is not None: + instance = context.process_output(instance) + return instance + + @staticmethod + def load_errors(data: dict | list, context: LoadContext | None) -> list[ValidationError]: + if isinstance(data, dict): + # convert simple named errors to list of ValidationError + result = [] + for k, v in data.items(): + if isinstance(v, dict): + # value is an object, spread its properties + result.append({"name": k, **v}) + else: + # value is a scalar, use it as the primary property + result.append({"name": k, "message": v}) + data = result + return [ValidationError.load(item, context) for item in data] + + @staticmethod + def save_errors(items: list[ValidationError], context: SaveContext | None) -> dict[str, Any] | list[dict[str, Any]]: + if context is None: + context = SaveContext() + + # This type doesn't have a 'name' property, so always use array format + return [item.save(context) for item in items] + + def save(self, context: SaveContext | None = None) -> dict[str, Any]: + """Save the ValidationResult instance to a dictionary. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + Returns: + dict[str, Any]: The dictionary representation of this instance. + + """ + obj = self + if context is not None: + obj = context.process_object(obj) + + result: dict[str, Any] = {} + + if obj.valid is not None: + result["valid"] = obj.valid + if obj.errors is not None: + result["errors"] = ValidationResult.save_errors(obj.errors, context) + + if context is not None: + result = context.process_dict(result) + return result + + def to_yaml(self, context: SaveContext | None = None) -> str: + """Convert the ValidationResult instance to a YAML string. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + Returns: + str: The YAML string representation of this instance. + + """ + if context is None: + context = SaveContext() + return context.to_yaml(self.save(context)) + + def to_json(self, context: SaveContext | None = None, indent: int = 2) -> str: + """Convert the ValidationResult instance to a JSON string. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + indent (int): Number of spaces for indentation. Defaults to 2. + Returns: + str: The JSON string representation of this instance. + + """ + if context is None: + context = SaveContext() + return context.to_json(self.save(context), indent) diff --git a/runtime/python/prompty/prompty/model/core/__init__.py b/runtime/python/prompty/prompty/model/core/__init__.py index 98a61477..31fff0b6 100644 --- a/runtime/python/prompty/prompty/model/core/__init__.py +++ b/runtime/python/prompty/prompty/model/core/__init__.py @@ -3,14 +3,22 @@ # DO NOT EDIT THIS FILE DIRECTLY # ANY EDITS WILL BE LOST ########################################## +from ._FileNotFoundError import FileNotFoundError +from ._InvokerError import InvokerError from ._Property import ( ArrayProperty, ObjectProperty, Property, ) +from ._ValidationError import ValidationError +from ._ValidationResult import ValidationResult __all__ = [ "Property", "ArrayProperty", "ObjectProperty", + "FileNotFoundError", + "InvokerError", + "ValidationError", + "ValidationResult", ] diff --git a/runtime/python/prompty/prompty/model/streaming/_StreamOptions.py b/runtime/python/prompty/prompty/model/streaming/_StreamOptions.py new file mode 100644 index 00000000..36881dc9 --- /dev/null +++ b/runtime/python/prompty/prompty/model/streaming/_StreamOptions.py @@ -0,0 +1,98 @@ +########################################## +# WARNING: This is an auto-generated file. +# DO NOT EDIT THIS FILE DIRECTLY +# ANY EDITS WILL BE LOST +########################################## + +from dataclasses import dataclass +from typing import Any, ClassVar + +from .._context import LoadContext, SaveContext + + +@dataclass +class StreamOptions: + """Options controlling streaming behavior for LLM API calls. + Passed alongside the model options when streaming is enabled. + + Attributes + ---------- + include_usage : Optional[bool] + When true, the final streaming chunk includes token usage statistics + """ + + _shorthand_property: ClassVar[str | None] = None + + include_usage: bool | None = None + + @staticmethod + def load(data: Any, context: LoadContext | None = None) -> "StreamOptions": + """Load a StreamOptions instance. + Args: + data (Any): The data to load the instance from. + context (Optional[LoadContext]): Optional context with pre/post processing callbacks. + Returns: + StreamOptions: The loaded StreamOptions instance. + + """ + + if context is not None: + data = context.process_input(data) + + if not isinstance(data, dict): + raise ValueError(f"Invalid data for StreamOptions: {data}") + + # create new instance + instance = StreamOptions() + + if data is not None and "includeUsage" in data: + instance.include_usage = data["includeUsage"] + if context is not None: + instance = context.process_output(instance) + return instance + + def save(self, context: SaveContext | None = None) -> dict[str, Any]: + """Save the StreamOptions instance to a dictionary. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + Returns: + dict[str, Any]: The dictionary representation of this instance. + + """ + obj = self + if context is not None: + obj = context.process_object(obj) + + result: dict[str, Any] = {} + + if obj.include_usage is not None: + result["includeUsage"] = obj.include_usage + + if context is not None: + result = context.process_dict(result) + return result + + def to_yaml(self, context: SaveContext | None = None) -> str: + """Convert the StreamOptions instance to a YAML string. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + Returns: + str: The YAML string representation of this instance. + + """ + if context is None: + context = SaveContext() + return context.to_yaml(self.save(context)) + + def to_json(self, context: SaveContext | None = None, indent: int = 2) -> str: + """Convert the StreamOptions instance to a JSON string. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + indent (int): Number of spaces for indentation. Defaults to 2. + Returns: + str: The JSON string representation of this instance. + + """ + if context is None: + context = SaveContext() + return context.to_json(self.save(context), indent) diff --git a/runtime/python/prompty/prompty/model/streaming/__init__.py b/runtime/python/prompty/prompty/model/streaming/__init__.py new file mode 100644 index 00000000..099ef532 --- /dev/null +++ b/runtime/python/prompty/prompty/model/streaming/__init__.py @@ -0,0 +1,10 @@ +########################################## +# WARNING: This is an auto-generated file. +# DO NOT EDIT THIS FILE DIRECTLY +# ANY EDITS WILL BE LOST +########################################## +from ._StreamOptions import StreamOptions + +__all__ = [ + "StreamOptions", +] diff --git a/runtime/python/prompty/prompty/model/tracing/_TraceFile.py b/runtime/python/prompty/prompty/model/tracing/_TraceFile.py new file mode 100644 index 00000000..9967bb0a --- /dev/null +++ b/runtime/python/prompty/prompty/model/tracing/_TraceFile.py @@ -0,0 +1,112 @@ +########################################## +# WARNING: This is an auto-generated file. +# DO NOT EDIT THIS FILE DIRECTLY +# ANY EDITS WILL BE LOST +########################################## + +from dataclasses import dataclass, field +from typing import Any, ClassVar + +from .._context import LoadContext, SaveContext +from ._TraceSpan import TraceSpan + + +@dataclass +class TraceFile: + """The top-level .tracy file structure written by the file backend (§3.6.1). + + Attributes + ---------- + runtime : str + Language/runtime name (e.g., 'python', 'csharp', 'javascript') + version : str + Prompty library version + trace : TraceSpan + The root trace span + """ + + _shorthand_property: ClassVar[str | None] = None + + runtime: str = field(default="") + version: str = field(default="") + trace: TraceSpan = field(default_factory=TraceSpan) + + @staticmethod + def load(data: Any, context: LoadContext | None = None) -> "TraceFile": + """Load a TraceFile instance. + Args: + data (Any): The data to load the instance from. + context (Optional[LoadContext]): Optional context with pre/post processing callbacks. + Returns: + TraceFile: The loaded TraceFile instance. + + """ + + if context is not None: + data = context.process_input(data) + + if not isinstance(data, dict): + raise ValueError(f"Invalid data for TraceFile: {data}") + + # create new instance + instance = TraceFile() + + if data is not None and "runtime" in data: + instance.runtime = data["runtime"] + if data is not None and "version" in data: + instance.version = data["version"] + if data is not None and "trace" in data: + instance.trace = TraceSpan.load(data["trace"], context) + if context is not None: + instance = context.process_output(instance) + return instance + + def save(self, context: SaveContext | None = None) -> dict[str, Any]: + """Save the TraceFile instance to a dictionary. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + Returns: + dict[str, Any]: The dictionary representation of this instance. + + """ + obj = self + if context is not None: + obj = context.process_object(obj) + + result: dict[str, Any] = {} + + if obj.runtime is not None: + result["runtime"] = obj.runtime + if obj.version is not None: + result["version"] = obj.version + if obj.trace is not None: + result["trace"] = obj.trace.save(context) + + if context is not None: + result = context.process_dict(result) + return result + + def to_yaml(self, context: SaveContext | None = None) -> str: + """Convert the TraceFile instance to a YAML string. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + Returns: + str: The YAML string representation of this instance. + + """ + if context is None: + context = SaveContext() + return context.to_yaml(self.save(context)) + + def to_json(self, context: SaveContext | None = None, indent: int = 2) -> str: + """Convert the TraceFile instance to a JSON string. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + indent (int): Number of spaces for indentation. Defaults to 2. + Returns: + str: The JSON string representation of this instance. + + """ + if context is None: + context = SaveContext() + return context.to_json(self.save(context), indent) diff --git a/runtime/python/prompty/prompty/model/tracing/_TraceSpan.py b/runtime/python/prompty/prompty/model/tracing/_TraceSpan.py new file mode 100644 index 00000000..386fb5aa --- /dev/null +++ b/runtime/python/prompty/prompty/model/tracing/_TraceSpan.py @@ -0,0 +1,157 @@ +########################################## +# WARNING: This is an auto-generated file. +# DO NOT EDIT THIS FILE DIRECTLY +# ANY EDITS WILL BE LOST +########################################## + +from dataclasses import dataclass, field +from typing import Any, ClassVar + +from .._context import LoadContext, SaveContext +from ..model._TokenUsage import TokenUsage +from ._TraceTime import TraceTime + + +@dataclass +class TraceSpan: + """A single trace span capturing one pipeline stage or function invocation. + Spans nest via the `__frames` field to form a tree representing the + full execution (§3.6.1). + + Attributes + ---------- + name : str + The name of this span (typically the function signature) + __time : TraceTime + Timing information for this span + signature : Optional[str] + Fully-qualified function signature that produced this span + inputs : Optional[dict[str, Any]] + Serialized input parameters (redacted per §3.4) + output : Optional[Any] + Serialized return value or error information (redacted per §3.4) + error : Optional[str] + Error message if the span ended with an exception + __usage : Optional[TokenUsage] + Aggregated token usage hoisted from child spans (§3.5) + attributes : Optional[dict[str, Any]] + Additional span attributes (e.g., OpenTelemetry GenAI attributes) + __frames : Optional[list[Any]] + Nested child spans forming the execution tree (recursive; each element is a TraceSpan) + """ + + _shorthand_property: ClassVar[str | None] = None + + name: str = field(default="") + __time: TraceTime = field(default_factory=TraceTime) + signature: str | None = None + inputs: dict[str, Any] | None = None + output: Any | None = None + error: str | None = None + __usage: TokenUsage | None = None + attributes: dict[str, Any] | None = None + __frames: list[Any] = field(default_factory=list) + + @staticmethod + def load(data: Any, context: LoadContext | None = None) -> "TraceSpan": + """Load a TraceSpan instance. + Args: + data (Any): The data to load the instance from. + context (Optional[LoadContext]): Optional context with pre/post processing callbacks. + Returns: + TraceSpan: The loaded TraceSpan instance. + + """ + + if context is not None: + data = context.process_input(data) + + if not isinstance(data, dict): + raise ValueError(f"Invalid data for TraceSpan: {data}") + + # create new instance + instance = TraceSpan() + + if data is not None and "name" in data: + instance.name = data["name"] + if data is not None and "__time" in data: + instance.__time = TraceTime.load(data["__time"], context) + if data is not None and "signature" in data: + instance.signature = data["signature"] + if data is not None and "inputs" in data: + instance.inputs = data["inputs"] + if data is not None and "output" in data: + instance.output = data["output"] + if data is not None and "error" in data: + instance.error = data["error"] + if data is not None and "__usage" in data: + instance.__usage = TokenUsage.load(data["__usage"], context) + if data is not None and "attributes" in data: + instance.attributes = data["attributes"] + if data is not None and "__frames" in data: + instance.__frames = data["__frames"] + if context is not None: + instance = context.process_output(instance) + return instance + + def save(self, context: SaveContext | None = None) -> dict[str, Any]: + """Save the TraceSpan instance to a dictionary. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + Returns: + dict[str, Any]: The dictionary representation of this instance. + + """ + obj = self + if context is not None: + obj = context.process_object(obj) + + result: dict[str, Any] = {} + + if obj.name is not None: + result["name"] = obj.name + if obj.__time is not None: + result["__time"] = obj.__time.save(context) + if obj.signature is not None: + result["signature"] = obj.signature + if obj.inputs is not None: + result["inputs"] = obj.inputs + if obj.output is not None: + result["output"] = obj.output + if obj.error is not None: + result["error"] = obj.error + if obj.__usage is not None: + result["__usage"] = obj.__usage.save(context) + if obj.attributes is not None: + result["attributes"] = obj.attributes + if obj.__frames is not None: + result["__frames"] = obj.__frames + + if context is not None: + result = context.process_dict(result) + return result + + def to_yaml(self, context: SaveContext | None = None) -> str: + """Convert the TraceSpan instance to a YAML string. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + Returns: + str: The YAML string representation of this instance. + + """ + if context is None: + context = SaveContext() + return context.to_yaml(self.save(context)) + + def to_json(self, context: SaveContext | None = None, indent: int = 2) -> str: + """Convert the TraceSpan instance to a JSON string. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + indent (int): Number of spaces for indentation. Defaults to 2. + Returns: + str: The JSON string representation of this instance. + + """ + if context is None: + context = SaveContext() + return context.to_json(self.save(context), indent) diff --git a/runtime/python/prompty/prompty/model/tracing/_TraceTime.py b/runtime/python/prompty/prompty/model/tracing/_TraceTime.py new file mode 100644 index 00000000..ff208518 --- /dev/null +++ b/runtime/python/prompty/prompty/model/tracing/_TraceTime.py @@ -0,0 +1,111 @@ +########################################## +# WARNING: This is an auto-generated file. +# DO NOT EDIT THIS FILE DIRECTLY +# ANY EDITS WILL BE LOST +########################################## + +from dataclasses import dataclass, field +from typing import Any, ClassVar + +from .._context import LoadContext, SaveContext + + +@dataclass +class TraceTime: + """Timing information for a trace span. + + Attributes + ---------- + start : str + ISO 8601 UTC timestamp when the span started + end : str + ISO 8601 UTC timestamp when the span ended + duration : float + Duration of the span in milliseconds + """ + + _shorthand_property: ClassVar[str | None] = None + + start: str = field(default="") + end: str = field(default="") + duration: float = field(default=0.0) + + @staticmethod + def load(data: Any, context: LoadContext | None = None) -> "TraceTime": + """Load a TraceTime instance. + Args: + data (Any): The data to load the instance from. + context (Optional[LoadContext]): Optional context with pre/post processing callbacks. + Returns: + TraceTime: The loaded TraceTime instance. + + """ + + if context is not None: + data = context.process_input(data) + + if not isinstance(data, dict): + raise ValueError(f"Invalid data for TraceTime: {data}") + + # create new instance + instance = TraceTime() + + if data is not None and "start" in data: + instance.start = data["start"] + if data is not None and "end" in data: + instance.end = data["end"] + if data is not None and "duration" in data: + instance.duration = data["duration"] + if context is not None: + instance = context.process_output(instance) + return instance + + def save(self, context: SaveContext | None = None) -> dict[str, Any]: + """Save the TraceTime instance to a dictionary. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + Returns: + dict[str, Any]: The dictionary representation of this instance. + + """ + obj = self + if context is not None: + obj = context.process_object(obj) + + result: dict[str, Any] = {} + + if obj.start is not None: + result["start"] = obj.start + if obj.end is not None: + result["end"] = obj.end + if obj.duration is not None: + result["duration"] = obj.duration + + if context is not None: + result = context.process_dict(result) + return result + + def to_yaml(self, context: SaveContext | None = None) -> str: + """Convert the TraceTime instance to a YAML string. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + Returns: + str: The YAML string representation of this instance. + + """ + if context is None: + context = SaveContext() + return context.to_yaml(self.save(context)) + + def to_json(self, context: SaveContext | None = None, indent: int = 2) -> str: + """Convert the TraceTime instance to a JSON string. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + indent (int): Number of spaces for indentation. Defaults to 2. + Returns: + str: The JSON string representation of this instance. + + """ + if context is None: + context = SaveContext() + return context.to_json(self.save(context), indent) diff --git a/runtime/python/prompty/prompty/model/tracing/__init__.py b/runtime/python/prompty/prompty/model/tracing/__init__.py new file mode 100644 index 00000000..7eb0a530 --- /dev/null +++ b/runtime/python/prompty/prompty/model/tracing/__init__.py @@ -0,0 +1,14 @@ +########################################## +# WARNING: This is an auto-generated file. +# DO NOT EDIT THIS FILE DIRECTLY +# ANY EDITS WILL BE LOST +########################################## +from ._TraceFile import TraceFile +from ._TraceSpan import TraceSpan +from ._TraceTime import TraceTime + +__all__ = [ + "TraceTime", + "TraceSpan", + "TraceFile", +] diff --git a/runtime/python/prompty/prompty/model/wire/_AnthropicImageBlock.py b/runtime/python/prompty/prompty/model/wire/_AnthropicImageBlock.py new file mode 100644 index 00000000..63d6a74c --- /dev/null +++ b/runtime/python/prompty/prompty/model/wire/_AnthropicImageBlock.py @@ -0,0 +1,106 @@ +########################################## +# WARNING: This is an auto-generated file. +# DO NOT EDIT THIS FILE DIRECTLY +# ANY EDITS WILL BE LOST +########################################## + +from dataclasses import dataclass, field +from typing import Any, ClassVar + +from .._context import LoadContext, SaveContext +from ._AnthropicImageSource import AnthropicImageSource + + +@dataclass +class AnthropicImageBlock: + """An image content block using base64-encoded data. + Anthropic requires images as base64 with an explicit media type. + + Attributes + ---------- + type : str + The content block type + source : AnthropicImageSource + The image source (base64-encoded) + """ + + _shorthand_property: ClassVar[str | None] = None + + type: str = field(default="image") + source: AnthropicImageSource = field(default_factory=AnthropicImageSource) + + @staticmethod + def load(data: Any, context: LoadContext | None = None) -> "AnthropicImageBlock": + """Load a AnthropicImageBlock instance. + Args: + data (Any): The data to load the instance from. + context (Optional[LoadContext]): Optional context with pre/post processing callbacks. + Returns: + AnthropicImageBlock: The loaded AnthropicImageBlock instance. + + """ + + if context is not None: + data = context.process_input(data) + + if not isinstance(data, dict): + raise ValueError(f"Invalid data for AnthropicImageBlock: {data}") + + # create new instance + instance = AnthropicImageBlock() + + if data is not None and "type" in data: + instance.type = data["type"] + if data is not None and "source" in data: + instance.source = AnthropicImageSource.load(data["source"], context) + if context is not None: + instance = context.process_output(instance) + return instance + + def save(self, context: SaveContext | None = None) -> dict[str, Any]: + """Save the AnthropicImageBlock instance to a dictionary. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + Returns: + dict[str, Any]: The dictionary representation of this instance. + + """ + obj = self + if context is not None: + obj = context.process_object(obj) + + result: dict[str, Any] = {} + + if obj.type is not None: + result["type"] = obj.type + if obj.source is not None: + result["source"] = obj.source.save(context) + + if context is not None: + result = context.process_dict(result) + return result + + def to_yaml(self, context: SaveContext | None = None) -> str: + """Convert the AnthropicImageBlock instance to a YAML string. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + Returns: + str: The YAML string representation of this instance. + + """ + if context is None: + context = SaveContext() + return context.to_yaml(self.save(context)) + + def to_json(self, context: SaveContext | None = None, indent: int = 2) -> str: + """Convert the AnthropicImageBlock instance to a JSON string. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + indent (int): Number of spaces for indentation. Defaults to 2. + Returns: + str: The JSON string representation of this instance. + + """ + if context is None: + context = SaveContext() + return context.to_json(self.save(context), indent) diff --git a/runtime/python/prompty/prompty/model/wire/_AnthropicImageSource.py b/runtime/python/prompty/prompty/model/wire/_AnthropicImageSource.py new file mode 100644 index 00000000..9b6ba862 --- /dev/null +++ b/runtime/python/prompty/prompty/model/wire/_AnthropicImageSource.py @@ -0,0 +1,111 @@ +########################################## +# WARNING: This is an auto-generated file. +# DO NOT EDIT THIS FILE DIRECTLY +# ANY EDITS WILL BE LOST +########################################## + +from dataclasses import dataclass, field +from typing import Any, ClassVar + +from .._context import LoadContext, SaveContext + + +@dataclass +class AnthropicImageSource: + """Source descriptor for an Anthropic base64 image. + + Attributes + ---------- + type : str + The encoding type (always 'base64' for inline images) + media_type : str + The MIME type of the image (e.g., 'image/png', 'image/jpeg') + data : str + The base64-encoded image data + """ + + _shorthand_property: ClassVar[str | None] = None + + type: str = field(default="base64") + media_type: str = field(default="") + data: str = field(default="") + + @staticmethod + def load(data: Any, context: LoadContext | None = None) -> "AnthropicImageSource": + """Load a AnthropicImageSource instance. + Args: + data (Any): The data to load the instance from. + context (Optional[LoadContext]): Optional context with pre/post processing callbacks. + Returns: + AnthropicImageSource: The loaded AnthropicImageSource instance. + + """ + + if context is not None: + data = context.process_input(data) + + if not isinstance(data, dict): + raise ValueError(f"Invalid data for AnthropicImageSource: {data}") + + # create new instance + instance = AnthropicImageSource() + + if data is not None and "type" in data: + instance.type = data["type"] + if data is not None and "media_type" in data: + instance.media_type = data["media_type"] + if data is not None and "data" in data: + instance.data = data["data"] + if context is not None: + instance = context.process_output(instance) + return instance + + def save(self, context: SaveContext | None = None) -> dict[str, Any]: + """Save the AnthropicImageSource instance to a dictionary. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + Returns: + dict[str, Any]: The dictionary representation of this instance. + + """ + obj = self + if context is not None: + obj = context.process_object(obj) + + result: dict[str, Any] = {} + + if obj.type is not None: + result["type"] = obj.type + if obj.media_type is not None: + result["media_type"] = obj.media_type + if obj.data is not None: + result["data"] = obj.data + + if context is not None: + result = context.process_dict(result) + return result + + def to_yaml(self, context: SaveContext | None = None) -> str: + """Convert the AnthropicImageSource instance to a YAML string. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + Returns: + str: The YAML string representation of this instance. + + """ + if context is None: + context = SaveContext() + return context.to_yaml(self.save(context)) + + def to_json(self, context: SaveContext | None = None, indent: int = 2) -> str: + """Convert the AnthropicImageSource instance to a JSON string. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + indent (int): Number of spaces for indentation. Defaults to 2. + Returns: + str: The JSON string representation of this instance. + + """ + if context is None: + context = SaveContext() + return context.to_json(self.save(context), indent) diff --git a/runtime/python/prompty/prompty/model/wire/_AnthropicMessagesRequest.py b/runtime/python/prompty/prompty/model/wire/_AnthropicMessagesRequest.py new file mode 100644 index 00000000..6f430618 --- /dev/null +++ b/runtime/python/prompty/prompty/model/wire/_AnthropicMessagesRequest.py @@ -0,0 +1,225 @@ +########################################## +# WARNING: This is an auto-generated file. +# DO NOT EDIT THIS FILE DIRECTLY +# ANY EDITS WILL BE LOST +########################################## + +from dataclasses import dataclass, field +from typing import Any, ClassVar + +from .._context import LoadContext, SaveContext +from ._AnthropicToolDefinition import AnthropicToolDefinition +from ._AnthropicWireMessage import AnthropicWireMessage + + +@dataclass +class AnthropicMessagesRequest: + """The full request body for the Anthropic Messages API (§7.5). + + Attributes + ---------- + model : str + The model identifier + messages : list[AnthropicWireMessage] + The non-system messages to send + max_tokens : int + Maximum number of tokens to generate (required by Anthropic) + system : Optional[str] + System prompt text (extracted from system-role messages) + temperature : Optional[float] + Sampling temperature + top_p : Optional[float] + Top-P sampling value + top_k : Optional[int] + Top-K sampling value + stop_sequences : Optional[list[str]] + Stop sequences to end generation + tools : Optional[list[AnthropicToolDefinition]] + Tool definitions available to the model + """ + + _shorthand_property: ClassVar[str | None] = None + + model: str = field(default="") + messages: list[AnthropicWireMessage] = field(default_factory=list) + max_tokens: int = field(default=0) + system: str | None = None + temperature: float | None = None + top_p: float | None = None + top_k: int | None = None + stop_sequences: list[str] = field(default_factory=list) + tools: list[AnthropicToolDefinition] = field(default_factory=list) + + @staticmethod + def load(data: Any, context: LoadContext | None = None) -> "AnthropicMessagesRequest": + """Load a AnthropicMessagesRequest instance. + Args: + data (Any): The data to load the instance from. + context (Optional[LoadContext]): Optional context with pre/post processing callbacks. + Returns: + AnthropicMessagesRequest: The loaded AnthropicMessagesRequest instance. + + """ + + if context is not None: + data = context.process_input(data) + + if not isinstance(data, dict): + raise ValueError(f"Invalid data for AnthropicMessagesRequest: {data}") + + # create new instance + instance = AnthropicMessagesRequest() + + if data is not None and "model" in data: + instance.model = data["model"] + if data is not None and "messages" in data: + instance.messages = AnthropicMessagesRequest.load_messages(data["messages"], context) + if data is not None and "max_tokens" in data: + instance.max_tokens = data["max_tokens"] + if data is not None and "system" in data: + instance.system = data["system"] + if data is not None and "temperature" in data: + instance.temperature = data["temperature"] + if data is not None and "top_p" in data: + instance.top_p = data["top_p"] + if data is not None and "top_k" in data: + instance.top_k = data["top_k"] + if data is not None and "stop_sequences" in data: + instance.stop_sequences = data["stop_sequences"] + if data is not None and "tools" in data: + instance.tools = AnthropicMessagesRequest.load_tools(data["tools"], context) + if context is not None: + instance = context.process_output(instance) + return instance + + @staticmethod + def load_messages(data: dict | list, context: LoadContext | None) -> list[AnthropicWireMessage]: + if isinstance(data, dict): + # convert simple named messages to list of AnthropicWireMessage + result = [] + for k, v in data.items(): + if isinstance(v, dict): + # value is an object, spread its properties + result.append({"name": k, **v}) + else: + # value is a scalar, use it as the primary property + result.append({"name": k, "role": v}) + data = result + return [AnthropicWireMessage.load(item, context) for item in data] + + @staticmethod + def save_messages( + items: list[AnthropicWireMessage], context: SaveContext | None + ) -> dict[str, Any] | list[dict[str, Any]]: + if context is None: + context = SaveContext() + + # This type doesn't have a 'name' property, so always use array format + return [item.save(context) for item in items] + + @staticmethod + def load_tools(data: dict | list, context: LoadContext | None) -> list[AnthropicToolDefinition]: + if isinstance(data, dict): + # convert simple named tools to list of AnthropicToolDefinition + result = [] + for k, v in data.items(): + if isinstance(v, dict): + # value is an object, spread its properties + result.append({"name": k, **v}) + else: + # value is a scalar, use it as the primary property + result.append({"name": k, "description": v}) + data = result + return [AnthropicToolDefinition.load(item, context) for item in data] + + @staticmethod + def save_tools( + items: list[AnthropicToolDefinition], context: SaveContext | None + ) -> dict[str, Any] | list[dict[str, Any]]: + if context is None: + context = SaveContext() + + if context.collection_format == "array": + return [item.save(context) for item in items] + + # Object format: use name as key + result: dict[str, Any] = {} + for item in items: + item_data = item.save(context) + name = item_data.pop("name", None) + if name: + # Check if we can use shorthand (only primary property set) + if context.use_shorthand and hasattr(item, "_shorthand_property"): + shorthand_prop = item._shorthand_property + if shorthand_prop and len(item_data) == 1 and shorthand_prop in item_data: + result[name] = item_data[shorthand_prop] + continue + result[name] = item_data + else: + # No name, fall back to array format for this item + if "_unnamed" not in result: + result["_unnamed"] = [] + result["_unnamed"].append(item_data) + return result + + def save(self, context: SaveContext | None = None) -> dict[str, Any]: + """Save the AnthropicMessagesRequest instance to a dictionary. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + Returns: + dict[str, Any]: The dictionary representation of this instance. + + """ + obj = self + if context is not None: + obj = context.process_object(obj) + + result: dict[str, Any] = {} + + if obj.model is not None: + result["model"] = obj.model + if obj.messages is not None: + result["messages"] = AnthropicMessagesRequest.save_messages(obj.messages, context) + if obj.max_tokens is not None: + result["max_tokens"] = obj.max_tokens + if obj.system is not None: + result["system"] = obj.system + if obj.temperature is not None: + result["temperature"] = obj.temperature + if obj.top_p is not None: + result["top_p"] = obj.top_p + if obj.top_k is not None: + result["top_k"] = obj.top_k + if obj.stop_sequences is not None: + result["stop_sequences"] = obj.stop_sequences + if obj.tools is not None: + result["tools"] = AnthropicMessagesRequest.save_tools(obj.tools, context) + + if context is not None: + result = context.process_dict(result) + return result + + def to_yaml(self, context: SaveContext | None = None) -> str: + """Convert the AnthropicMessagesRequest instance to a YAML string. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + Returns: + str: The YAML string representation of this instance. + + """ + if context is None: + context = SaveContext() + return context.to_yaml(self.save(context)) + + def to_json(self, context: SaveContext | None = None, indent: int = 2) -> str: + """Convert the AnthropicMessagesRequest instance to a JSON string. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + indent (int): Number of spaces for indentation. Defaults to 2. + Returns: + str: The JSON string representation of this instance. + + """ + if context is None: + context = SaveContext() + return context.to_json(self.save(context), indent) diff --git a/runtime/python/prompty/prompty/model/wire/_AnthropicMessagesResponse.py b/runtime/python/prompty/prompty/model/wire/_AnthropicMessagesResponse.py new file mode 100644 index 00000000..8c02b899 --- /dev/null +++ b/runtime/python/prompty/prompty/model/wire/_AnthropicMessagesResponse.py @@ -0,0 +1,140 @@ +########################################## +# WARNING: This is an auto-generated file. +# DO NOT EDIT THIS FILE DIRECTLY +# ANY EDITS WILL BE LOST +########################################## + +from dataclasses import dataclass, field +from typing import Any, ClassVar + +from .._context import LoadContext, SaveContext +from ._AnthropicUsage import AnthropicUsage + + +@dataclass +class AnthropicMessagesResponse: + """The response body from the Anthropic Messages API. + + Attributes + ---------- + id : str + Unique response identifier + type : str + Object type (always 'message') + role : str + The role of the response (always 'assistant') + content : list[Any] + Array of content blocks in the response (AnthropicTextBlock | AnthropicToolUseBlock) + model : str + The model that generated the response + stop_reason : str + The reason generation stopped ('end_turn', 'max_tokens', 'stop_sequence', 'tool_use') + usage : AnthropicUsage + Token usage statistics + """ + + _shorthand_property: ClassVar[str | None] = None + + id: str = field(default="") + type: str = field(default="message") + role: str = field(default="assistant") + content: list[Any] = field(default_factory=list) + model: str = field(default="") + stop_reason: str = field(default="") + usage: AnthropicUsage = field(default_factory=AnthropicUsage) + + @staticmethod + def load(data: Any, context: LoadContext | None = None) -> "AnthropicMessagesResponse": + """Load a AnthropicMessagesResponse instance. + Args: + data (Any): The data to load the instance from. + context (Optional[LoadContext]): Optional context with pre/post processing callbacks. + Returns: + AnthropicMessagesResponse: The loaded AnthropicMessagesResponse instance. + + """ + + if context is not None: + data = context.process_input(data) + + if not isinstance(data, dict): + raise ValueError(f"Invalid data for AnthropicMessagesResponse: {data}") + + # create new instance + instance = AnthropicMessagesResponse() + + if data is not None and "id" in data: + instance.id = data["id"] + if data is not None and "type" in data: + instance.type = data["type"] + if data is not None and "role" in data: + instance.role = data["role"] + if data is not None and "content" in data: + instance.content = data["content"] + if data is not None and "model" in data: + instance.model = data["model"] + if data is not None and "stop_reason" in data: + instance.stop_reason = data["stop_reason"] + if data is not None and "usage" in data: + instance.usage = AnthropicUsage.load(data["usage"], context) + if context is not None: + instance = context.process_output(instance) + return instance + + def save(self, context: SaveContext | None = None) -> dict[str, Any]: + """Save the AnthropicMessagesResponse instance to a dictionary. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + Returns: + dict[str, Any]: The dictionary representation of this instance. + + """ + obj = self + if context is not None: + obj = context.process_object(obj) + + result: dict[str, Any] = {} + + if obj.id is not None: + result["id"] = obj.id + if obj.type is not None: + result["type"] = obj.type + if obj.role is not None: + result["role"] = obj.role + if obj.content is not None: + result["content"] = obj.content + if obj.model is not None: + result["model"] = obj.model + if obj.stop_reason is not None: + result["stop_reason"] = obj.stop_reason + if obj.usage is not None: + result["usage"] = obj.usage.save(context) + + if context is not None: + result = context.process_dict(result) + return result + + def to_yaml(self, context: SaveContext | None = None) -> str: + """Convert the AnthropicMessagesResponse instance to a YAML string. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + Returns: + str: The YAML string representation of this instance. + + """ + if context is None: + context = SaveContext() + return context.to_yaml(self.save(context)) + + def to_json(self, context: SaveContext | None = None, indent: int = 2) -> str: + """Convert the AnthropicMessagesResponse instance to a JSON string. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + indent (int): Number of spaces for indentation. Defaults to 2. + Returns: + str: The JSON string representation of this instance. + + """ + if context is None: + context = SaveContext() + return context.to_json(self.save(context), indent) diff --git a/runtime/python/prompty/prompty/model/wire/_AnthropicTextBlock.py b/runtime/python/prompty/prompty/model/wire/_AnthropicTextBlock.py new file mode 100644 index 00000000..ecf1c6d6 --- /dev/null +++ b/runtime/python/prompty/prompty/model/wire/_AnthropicTextBlock.py @@ -0,0 +1,104 @@ +########################################## +# WARNING: This is an auto-generated file. +# DO NOT EDIT THIS FILE DIRECTLY +# ANY EDITS WILL BE LOST +########################################## + +from dataclasses import dataclass, field +from typing import Any, ClassVar + +from .._context import LoadContext, SaveContext + + +@dataclass +class AnthropicTextBlock: + """A text content block in Anthropic's array-of-blocks message format. + + Attributes + ---------- + type : str + The content block type + text : str + The text content + """ + + _shorthand_property: ClassVar[str | None] = None + + type: str = field(default="text") + text: str = field(default="") + + @staticmethod + def load(data: Any, context: LoadContext | None = None) -> "AnthropicTextBlock": + """Load a AnthropicTextBlock instance. + Args: + data (Any): The data to load the instance from. + context (Optional[LoadContext]): Optional context with pre/post processing callbacks. + Returns: + AnthropicTextBlock: The loaded AnthropicTextBlock instance. + + """ + + if context is not None: + data = context.process_input(data) + + if not isinstance(data, dict): + raise ValueError(f"Invalid data for AnthropicTextBlock: {data}") + + # create new instance + instance = AnthropicTextBlock() + + if data is not None and "type" in data: + instance.type = data["type"] + if data is not None and "text" in data: + instance.text = data["text"] + if context is not None: + instance = context.process_output(instance) + return instance + + def save(self, context: SaveContext | None = None) -> dict[str, Any]: + """Save the AnthropicTextBlock instance to a dictionary. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + Returns: + dict[str, Any]: The dictionary representation of this instance. + + """ + obj = self + if context is not None: + obj = context.process_object(obj) + + result: dict[str, Any] = {} + + if obj.type is not None: + result["type"] = obj.type + if obj.text is not None: + result["text"] = obj.text + + if context is not None: + result = context.process_dict(result) + return result + + def to_yaml(self, context: SaveContext | None = None) -> str: + """Convert the AnthropicTextBlock instance to a YAML string. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + Returns: + str: The YAML string representation of this instance. + + """ + if context is None: + context = SaveContext() + return context.to_yaml(self.save(context)) + + def to_json(self, context: SaveContext | None = None, indent: int = 2) -> str: + """Convert the AnthropicTextBlock instance to a JSON string. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + indent (int): Number of spaces for indentation. Defaults to 2. + Returns: + str: The JSON string representation of this instance. + + """ + if context is None: + context = SaveContext() + return context.to_json(self.save(context), indent) diff --git a/runtime/python/prompty/prompty/model/wire/_AnthropicToolDefinition.py b/runtime/python/prompty/prompty/model/wire/_AnthropicToolDefinition.py new file mode 100644 index 00000000..531b9ec3 --- /dev/null +++ b/runtime/python/prompty/prompty/model/wire/_AnthropicToolDefinition.py @@ -0,0 +1,113 @@ +########################################## +# WARNING: This is an auto-generated file. +# DO NOT EDIT THIS FILE DIRECTLY +# ANY EDITS WILL BE LOST +########################################## + +from dataclasses import dataclass, field +from typing import Any, ClassVar + +from .._context import LoadContext, SaveContext + + +@dataclass +class AnthropicToolDefinition: + """A tool definition in Anthropic's format. Unlike OpenAI which wraps + tools in `{type: "function", function: {...}}`, Anthropic uses a + flat structure with `input_schema` (§7.5). + + Attributes + ---------- + name : str + The tool name + description : Optional[str] + A description of what the tool does + input_schema : dict[str, Any] + JSON Schema describing the tool's input parameters + """ + + _shorthand_property: ClassVar[str | None] = None + + name: str = field(default="") + description: str | None = None + input_schema: dict[str, Any] = field(default_factory=dict) + + @staticmethod + def load(data: Any, context: LoadContext | None = None) -> "AnthropicToolDefinition": + """Load a AnthropicToolDefinition instance. + Args: + data (Any): The data to load the instance from. + context (Optional[LoadContext]): Optional context with pre/post processing callbacks. + Returns: + AnthropicToolDefinition: The loaded AnthropicToolDefinition instance. + + """ + + if context is not None: + data = context.process_input(data) + + if not isinstance(data, dict): + raise ValueError(f"Invalid data for AnthropicToolDefinition: {data}") + + # create new instance + instance = AnthropicToolDefinition() + + if data is not None and "name" in data: + instance.name = data["name"] + if data is not None and "description" in data: + instance.description = data["description"] + if data is not None and "input_schema" in data: + instance.input_schema = data["input_schema"] + if context is not None: + instance = context.process_output(instance) + return instance + + def save(self, context: SaveContext | None = None) -> dict[str, Any]: + """Save the AnthropicToolDefinition instance to a dictionary. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + Returns: + dict[str, Any]: The dictionary representation of this instance. + + """ + obj = self + if context is not None: + obj = context.process_object(obj) + + result: dict[str, Any] = {} + + if obj.name is not None: + result["name"] = obj.name + if obj.description is not None: + result["description"] = obj.description + if obj.input_schema is not None: + result["input_schema"] = obj.input_schema + + if context is not None: + result = context.process_dict(result) + return result + + def to_yaml(self, context: SaveContext | None = None) -> str: + """Convert the AnthropicToolDefinition instance to a YAML string. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + Returns: + str: The YAML string representation of this instance. + + """ + if context is None: + context = SaveContext() + return context.to_yaml(self.save(context)) + + def to_json(self, context: SaveContext | None = None, indent: int = 2) -> str: + """Convert the AnthropicToolDefinition instance to a JSON string. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + indent (int): Number of spaces for indentation. Defaults to 2. + Returns: + str: The JSON string representation of this instance. + + """ + if context is None: + context = SaveContext() + return context.to_json(self.save(context), indent) diff --git a/runtime/python/prompty/prompty/model/wire/_AnthropicToolResultBlock.py b/runtime/python/prompty/prompty/model/wire/_AnthropicToolResultBlock.py new file mode 100644 index 00000000..bfed762a --- /dev/null +++ b/runtime/python/prompty/prompty/model/wire/_AnthropicToolResultBlock.py @@ -0,0 +1,111 @@ +########################################## +# WARNING: This is an auto-generated file. +# DO NOT EDIT THIS FILE DIRECTLY +# ANY EDITS WILL BE LOST +########################################## + +from dataclasses import dataclass, field +from typing import Any, ClassVar + +from .._context import LoadContext, SaveContext + + +@dataclass +class AnthropicToolResultBlock: + """A tool result content block sent back to the API with the tool's output. + + Attributes + ---------- + type : str + The content block type + tool_use_id : str + The tool_use id this result corresponds to + content : str + The tool's output content + """ + + _shorthand_property: ClassVar[str | None] = None + + type: str = field(default="tool_result") + tool_use_id: str = field(default="") + content: str = field(default="") + + @staticmethod + def load(data: Any, context: LoadContext | None = None) -> "AnthropicToolResultBlock": + """Load a AnthropicToolResultBlock instance. + Args: + data (Any): The data to load the instance from. + context (Optional[LoadContext]): Optional context with pre/post processing callbacks. + Returns: + AnthropicToolResultBlock: The loaded AnthropicToolResultBlock instance. + + """ + + if context is not None: + data = context.process_input(data) + + if not isinstance(data, dict): + raise ValueError(f"Invalid data for AnthropicToolResultBlock: {data}") + + # create new instance + instance = AnthropicToolResultBlock() + + if data is not None and "type" in data: + instance.type = data["type"] + if data is not None and "tool_use_id" in data: + instance.tool_use_id = data["tool_use_id"] + if data is not None and "content" in data: + instance.content = data["content"] + if context is not None: + instance = context.process_output(instance) + return instance + + def save(self, context: SaveContext | None = None) -> dict[str, Any]: + """Save the AnthropicToolResultBlock instance to a dictionary. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + Returns: + dict[str, Any]: The dictionary representation of this instance. + + """ + obj = self + if context is not None: + obj = context.process_object(obj) + + result: dict[str, Any] = {} + + if obj.type is not None: + result["type"] = obj.type + if obj.tool_use_id is not None: + result["tool_use_id"] = obj.tool_use_id + if obj.content is not None: + result["content"] = obj.content + + if context is not None: + result = context.process_dict(result) + return result + + def to_yaml(self, context: SaveContext | None = None) -> str: + """Convert the AnthropicToolResultBlock instance to a YAML string. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + Returns: + str: The YAML string representation of this instance. + + """ + if context is None: + context = SaveContext() + return context.to_yaml(self.save(context)) + + def to_json(self, context: SaveContext | None = None, indent: int = 2) -> str: + """Convert the AnthropicToolResultBlock instance to a JSON string. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + indent (int): Number of spaces for indentation. Defaults to 2. + Returns: + str: The JSON string representation of this instance. + + """ + if context is None: + context = SaveContext() + return context.to_json(self.save(context), indent) diff --git a/runtime/python/prompty/prompty/model/wire/_AnthropicToolUseBlock.py b/runtime/python/prompty/prompty/model/wire/_AnthropicToolUseBlock.py new file mode 100644 index 00000000..e6447659 --- /dev/null +++ b/runtime/python/prompty/prompty/model/wire/_AnthropicToolUseBlock.py @@ -0,0 +1,119 @@ +########################################## +# WARNING: This is an auto-generated file. +# DO NOT EDIT THIS FILE DIRECTLY +# ANY EDITS WILL BE LOST +########################################## + +from dataclasses import dataclass, field +from typing import Any, ClassVar + +from .._context import LoadContext, SaveContext + + +@dataclass +class AnthropicToolUseBlock: + """A tool use content block returned in an assistant message when + the model wants to invoke a tool. + + Attributes + ---------- + type : str + The content block type + id : str + Unique identifier for this tool invocation + name : str + The name of the tool to invoke + input : dict[str, Any] + The JSON arguments for the tool call + """ + + _shorthand_property: ClassVar[str | None] = None + + type: str = field(default="tool_use") + id: str = field(default="") + name: str = field(default="") + input: dict[str, Any] = field(default_factory=dict) + + @staticmethod + def load(data: Any, context: LoadContext | None = None) -> "AnthropicToolUseBlock": + """Load a AnthropicToolUseBlock instance. + Args: + data (Any): The data to load the instance from. + context (Optional[LoadContext]): Optional context with pre/post processing callbacks. + Returns: + AnthropicToolUseBlock: The loaded AnthropicToolUseBlock instance. + + """ + + if context is not None: + data = context.process_input(data) + + if not isinstance(data, dict): + raise ValueError(f"Invalid data for AnthropicToolUseBlock: {data}") + + # create new instance + instance = AnthropicToolUseBlock() + + if data is not None and "type" in data: + instance.type = data["type"] + if data is not None and "id" in data: + instance.id = data["id"] + if data is not None and "name" in data: + instance.name = data["name"] + if data is not None and "input" in data: + instance.input = data["input"] + if context is not None: + instance = context.process_output(instance) + return instance + + def save(self, context: SaveContext | None = None) -> dict[str, Any]: + """Save the AnthropicToolUseBlock instance to a dictionary. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + Returns: + dict[str, Any]: The dictionary representation of this instance. + + """ + obj = self + if context is not None: + obj = context.process_object(obj) + + result: dict[str, Any] = {} + + if obj.type is not None: + result["type"] = obj.type + if obj.id is not None: + result["id"] = obj.id + if obj.name is not None: + result["name"] = obj.name + if obj.input is not None: + result["input"] = obj.input + + if context is not None: + result = context.process_dict(result) + return result + + def to_yaml(self, context: SaveContext | None = None) -> str: + """Convert the AnthropicToolUseBlock instance to a YAML string. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + Returns: + str: The YAML string representation of this instance. + + """ + if context is None: + context = SaveContext() + return context.to_yaml(self.save(context)) + + def to_json(self, context: SaveContext | None = None, indent: int = 2) -> str: + """Convert the AnthropicToolUseBlock instance to a JSON string. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + indent (int): Number of spaces for indentation. Defaults to 2. + Returns: + str: The JSON string representation of this instance. + + """ + if context is None: + context = SaveContext() + return context.to_json(self.save(context), indent) diff --git a/runtime/python/prompty/prompty/model/wire/_AnthropicUsage.py b/runtime/python/prompty/prompty/model/wire/_AnthropicUsage.py new file mode 100644 index 00000000..68c45ff0 --- /dev/null +++ b/runtime/python/prompty/prompty/model/wire/_AnthropicUsage.py @@ -0,0 +1,104 @@ +########################################## +# WARNING: This is an auto-generated file. +# DO NOT EDIT THIS FILE DIRECTLY +# ANY EDITS WILL BE LOST +########################################## + +from dataclasses import dataclass, field +from typing import Any, ClassVar + +from .._context import LoadContext, SaveContext + + +@dataclass +class AnthropicUsage: + """Usage statistics returned in an Anthropic Messages API response. + + Attributes + ---------- + input_tokens : int + Number of input tokens consumed + output_tokens : int + Number of output tokens generated + """ + + _shorthand_property: ClassVar[str | None] = None + + input_tokens: int = field(default=0) + output_tokens: int = field(default=0) + + @staticmethod + def load(data: Any, context: LoadContext | None = None) -> "AnthropicUsage": + """Load a AnthropicUsage instance. + Args: + data (Any): The data to load the instance from. + context (Optional[LoadContext]): Optional context with pre/post processing callbacks. + Returns: + AnthropicUsage: The loaded AnthropicUsage instance. + + """ + + if context is not None: + data = context.process_input(data) + + if not isinstance(data, dict): + raise ValueError(f"Invalid data for AnthropicUsage: {data}") + + # create new instance + instance = AnthropicUsage() + + if data is not None and "input_tokens" in data: + instance.input_tokens = data["input_tokens"] + if data is not None and "output_tokens" in data: + instance.output_tokens = data["output_tokens"] + if context is not None: + instance = context.process_output(instance) + return instance + + def save(self, context: SaveContext | None = None) -> dict[str, Any]: + """Save the AnthropicUsage instance to a dictionary. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + Returns: + dict[str, Any]: The dictionary representation of this instance. + + """ + obj = self + if context is not None: + obj = context.process_object(obj) + + result: dict[str, Any] = {} + + if obj.input_tokens is not None: + result["input_tokens"] = obj.input_tokens + if obj.output_tokens is not None: + result["output_tokens"] = obj.output_tokens + + if context is not None: + result = context.process_dict(result) + return result + + def to_yaml(self, context: SaveContext | None = None) -> str: + """Convert the AnthropicUsage instance to a YAML string. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + Returns: + str: The YAML string representation of this instance. + + """ + if context is None: + context = SaveContext() + return context.to_yaml(self.save(context)) + + def to_json(self, context: SaveContext | None = None, indent: int = 2) -> str: + """Convert the AnthropicUsage instance to a JSON string. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + indent (int): Number of spaces for indentation. Defaults to 2. + Returns: + str: The JSON string representation of this instance. + + """ + if context is None: + context = SaveContext() + return context.to_json(self.save(context), indent) diff --git a/runtime/python/prompty/prompty/model/wire/_AnthropicWireMessage.py b/runtime/python/prompty/prompty/model/wire/_AnthropicWireMessage.py new file mode 100644 index 00000000..45911ef5 --- /dev/null +++ b/runtime/python/prompty/prompty/model/wire/_AnthropicWireMessage.py @@ -0,0 +1,106 @@ +########################################## +# WARNING: This is an auto-generated file. +# DO NOT EDIT THIS FILE DIRECTLY +# ANY EDITS WILL BE LOST +########################################## + +from dataclasses import dataclass, field +from typing import Any, ClassVar + +from .._context import LoadContext, SaveContext + + +@dataclass +class AnthropicWireMessage: + """A single message in the Anthropic Messages API wire format. + Anthropic always uses the array-of-blocks form for content, + even when there is only one text block (§7.5). + + Attributes + ---------- + role : str + The message role ('user' or 'assistant') + content : list[Any] + Array of typed content blocks (AnthropicTextBlock | AnthropicImageBlock | AnthropicToolUseBlock | AnthropicToolResultBlock) + """ + + _shorthand_property: ClassVar[str | None] = None + + role: str = field(default="") + content: list[Any] = field(default_factory=list) + + @staticmethod + def load(data: Any, context: LoadContext | None = None) -> "AnthropicWireMessage": + """Load a AnthropicWireMessage instance. + Args: + data (Any): The data to load the instance from. + context (Optional[LoadContext]): Optional context with pre/post processing callbacks. + Returns: + AnthropicWireMessage: The loaded AnthropicWireMessage instance. + + """ + + if context is not None: + data = context.process_input(data) + + if not isinstance(data, dict): + raise ValueError(f"Invalid data for AnthropicWireMessage: {data}") + + # create new instance + instance = AnthropicWireMessage() + + if data is not None and "role" in data: + instance.role = data["role"] + if data is not None and "content" in data: + instance.content = data["content"] + if context is not None: + instance = context.process_output(instance) + return instance + + def save(self, context: SaveContext | None = None) -> dict[str, Any]: + """Save the AnthropicWireMessage instance to a dictionary. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + Returns: + dict[str, Any]: The dictionary representation of this instance. + + """ + obj = self + if context is not None: + obj = context.process_object(obj) + + result: dict[str, Any] = {} + + if obj.role is not None: + result["role"] = obj.role + if obj.content is not None: + result["content"] = obj.content + + if context is not None: + result = context.process_dict(result) + return result + + def to_yaml(self, context: SaveContext | None = None) -> str: + """Convert the AnthropicWireMessage instance to a YAML string. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + Returns: + str: The YAML string representation of this instance. + + """ + if context is None: + context = SaveContext() + return context.to_yaml(self.save(context)) + + def to_json(self, context: SaveContext | None = None, indent: int = 2) -> str: + """Convert the AnthropicWireMessage instance to a JSON string. + Args: + context (Optional[SaveContext]): Optional context with pre/post processing callbacks. + indent (int): Number of spaces for indentation. Defaults to 2. + Returns: + str: The JSON string representation of this instance. + + """ + if context is None: + context = SaveContext() + return context.to_json(self.save(context), indent) diff --git a/runtime/python/prompty/prompty/model/wire/__init__.py b/runtime/python/prompty/prompty/model/wire/__init__.py new file mode 100644 index 00000000..ec174f95 --- /dev/null +++ b/runtime/python/prompty/prompty/model/wire/__init__.py @@ -0,0 +1,28 @@ +########################################## +# WARNING: This is an auto-generated file. +# DO NOT EDIT THIS FILE DIRECTLY +# ANY EDITS WILL BE LOST +########################################## +from ._AnthropicImageBlock import AnthropicImageBlock +from ._AnthropicImageSource import AnthropicImageSource +from ._AnthropicMessagesRequest import AnthropicMessagesRequest +from ._AnthropicMessagesResponse import AnthropicMessagesResponse +from ._AnthropicTextBlock import AnthropicTextBlock +from ._AnthropicToolDefinition import AnthropicToolDefinition +from ._AnthropicToolResultBlock import AnthropicToolResultBlock +from ._AnthropicToolUseBlock import AnthropicToolUseBlock +from ._AnthropicUsage import AnthropicUsage +from ._AnthropicWireMessage import AnthropicWireMessage + +__all__ = [ + "AnthropicTextBlock", + "AnthropicImageSource", + "AnthropicImageBlock", + "AnthropicToolUseBlock", + "AnthropicToolResultBlock", + "AnthropicWireMessage", + "AnthropicToolDefinition", + "AnthropicMessagesRequest", + "AnthropicUsage", + "AnthropicMessagesResponse", +] diff --git a/runtime/python/prompty/tests/model/core/test_file_not_found_error.py b/runtime/python/prompty/tests/model/core/test_file_not_found_error.py new file mode 100644 index 00000000..4151e7c4 --- /dev/null +++ b/runtime/python/prompty/tests/model/core/test_file_not_found_error.py @@ -0,0 +1,81 @@ +import json + +import yaml + +from prompty.model import FileNotFoundError + + +def test_load_json_filenotfounderror(): + json_data = r""" + { + "message": "Prompty file not found: ./chat.prompty", + "path": "./chat.prompty" + } + """ + data = json.loads(json_data, strict=False) + instance = FileNotFoundError.load(data) + assert instance is not None + assert instance.message == "Prompty file not found: ./chat.prompty" + assert instance.path == "./chat.prompty" + + +def test_load_yaml_filenotfounderror(): + yaml_data = r""" + message: "Prompty file not found: ./chat.prompty" + path: ./chat.prompty + + """ + data = yaml.load(yaml_data, Loader=yaml.FullLoader) + instance = FileNotFoundError.load(data) + assert instance is not None + assert instance.message == "Prompty file not found: ./chat.prompty" + assert instance.path == "./chat.prompty" + + +def test_roundtrip_json_filenotfounderror(): + """Test that load -> save -> load produces equivalent data.""" + json_data = r""" + { + "message": "Prompty file not found: ./chat.prompty", + "path": "./chat.prompty" + } + """ + original_data = json.loads(json_data, strict=False) + instance = FileNotFoundError.load(original_data) + saved_data = instance.save() + reloaded = FileNotFoundError.load(saved_data) + assert reloaded is not None + assert reloaded.message == "Prompty file not found: ./chat.prompty" + assert reloaded.path == "./chat.prompty" + + +def test_to_json_filenotfounderror(): + """Test that to_json produces valid JSON.""" + json_data = r""" + { + "message": "Prompty file not found: ./chat.prompty", + "path": "./chat.prompty" + } + """ + data = json.loads(json_data, strict=False) + instance = FileNotFoundError.load(data) + json_output = instance.to_json() + assert json_output is not None + parsed = json.loads(json_output) + assert isinstance(parsed, dict) + + +def test_to_yaml_filenotfounderror(): + """Test that to_yaml produces valid YAML.""" + json_data = r""" + { + "message": "Prompty file not found: ./chat.prompty", + "path": "./chat.prompty" + } + """ + data = json.loads(json_data, strict=False) + instance = FileNotFoundError.load(data) + yaml_output = instance.to_yaml() + assert yaml_output is not None + parsed = yaml.safe_load(yaml_output) + assert isinstance(parsed, dict) diff --git a/runtime/python/prompty/tests/model/core/test_invoker_error.py b/runtime/python/prompty/tests/model/core/test_invoker_error.py new file mode 100644 index 00000000..f179e211 --- /dev/null +++ b/runtime/python/prompty/tests/model/core/test_invoker_error.py @@ -0,0 +1,89 @@ +import json + +import yaml + +from prompty.model import InvokerError + + +def test_load_json_invokererror(): + json_data = r""" + { + "message": "No renderer registered for key: jinja2", + "component": "renderer", + "key": "jinja2" + } + """ + data = json.loads(json_data, strict=False) + instance = InvokerError.load(data) + assert instance is not None + assert instance.message == "No renderer registered for key: jinja2" + assert instance.component == "renderer" + assert instance.key == "jinja2" + + +def test_load_yaml_invokererror(): + yaml_data = r""" + message: "No renderer registered for key: jinja2" + component: renderer + key: jinja2 + + """ + data = yaml.load(yaml_data, Loader=yaml.FullLoader) + instance = InvokerError.load(data) + assert instance is not None + assert instance.message == "No renderer registered for key: jinja2" + assert instance.component == "renderer" + assert instance.key == "jinja2" + + +def test_roundtrip_json_invokererror(): + """Test that load -> save -> load produces equivalent data.""" + json_data = r""" + { + "message": "No renderer registered for key: jinja2", + "component": "renderer", + "key": "jinja2" + } + """ + original_data = json.loads(json_data, strict=False) + instance = InvokerError.load(original_data) + saved_data = instance.save() + reloaded = InvokerError.load(saved_data) + assert reloaded is not None + assert reloaded.message == "No renderer registered for key: jinja2" + assert reloaded.component == "renderer" + assert reloaded.key == "jinja2" + + +def test_to_json_invokererror(): + """Test that to_json produces valid JSON.""" + json_data = r""" + { + "message": "No renderer registered for key: jinja2", + "component": "renderer", + "key": "jinja2" + } + """ + data = json.loads(json_data, strict=False) + instance = InvokerError.load(data) + json_output = instance.to_json() + assert json_output is not None + parsed = json.loads(json_output) + assert isinstance(parsed, dict) + + +def test_to_yaml_invokererror(): + """Test that to_yaml produces valid YAML.""" + json_data = r""" + { + "message": "No renderer registered for key: jinja2", + "component": "renderer", + "key": "jinja2" + } + """ + data = json.loads(json_data, strict=False) + instance = InvokerError.load(data) + yaml_output = instance.to_yaml() + assert yaml_output is not None + parsed = yaml.safe_load(yaml_output) + assert isinstance(parsed, dict) diff --git a/runtime/python/prompty/tests/model/core/test_validation_error.py b/runtime/python/prompty/tests/model/core/test_validation_error.py new file mode 100644 index 00000000..4c7191b8 --- /dev/null +++ b/runtime/python/prompty/tests/model/core/test_validation_error.py @@ -0,0 +1,89 @@ +import json + +import yaml + +from prompty.model import ValidationError + + +def test_load_json_validationerror(): + json_data = r""" + { + "message": "Missing required input: firstName", + "property": "firstName", + "constraint": "required" + } + """ + data = json.loads(json_data, strict=False) + instance = ValidationError.load(data) + assert instance is not None + assert instance.message == "Missing required input: firstName" + assert instance.property == "firstName" + assert instance.constraint == "required" + + +def test_load_yaml_validationerror(): + yaml_data = r""" + message: "Missing required input: firstName" + property: firstName + constraint: required + + """ + data = yaml.load(yaml_data, Loader=yaml.FullLoader) + instance = ValidationError.load(data) + assert instance is not None + assert instance.message == "Missing required input: firstName" + assert instance.property == "firstName" + assert instance.constraint == "required" + + +def test_roundtrip_json_validationerror(): + """Test that load -> save -> load produces equivalent data.""" + json_data = r""" + { + "message": "Missing required input: firstName", + "property": "firstName", + "constraint": "required" + } + """ + original_data = json.loads(json_data, strict=False) + instance = ValidationError.load(original_data) + saved_data = instance.save() + reloaded = ValidationError.load(saved_data) + assert reloaded is not None + assert reloaded.message == "Missing required input: firstName" + assert reloaded.property == "firstName" + assert reloaded.constraint == "required" + + +def test_to_json_validationerror(): + """Test that to_json produces valid JSON.""" + json_data = r""" + { + "message": "Missing required input: firstName", + "property": "firstName", + "constraint": "required" + } + """ + data = json.loads(json_data, strict=False) + instance = ValidationError.load(data) + json_output = instance.to_json() + assert json_output is not None + parsed = json.loads(json_output) + assert isinstance(parsed, dict) + + +def test_to_yaml_validationerror(): + """Test that to_yaml produces valid YAML.""" + json_data = r""" + { + "message": "Missing required input: firstName", + "property": "firstName", + "constraint": "required" + } + """ + data = json.loads(json_data, strict=False) + instance = ValidationError.load(data) + yaml_output = instance.to_yaml() + assert yaml_output is not None + parsed = yaml.safe_load(yaml_output) + assert isinstance(parsed, dict) diff --git a/runtime/python/prompty/tests/model/core/test_validation_result.py b/runtime/python/prompty/tests/model/core/test_validation_result.py new file mode 100644 index 00000000..dbd693f9 --- /dev/null +++ b/runtime/python/prompty/tests/model/core/test_validation_result.py @@ -0,0 +1,78 @@ +import json + +import yaml + +from prompty.model import ValidationResult + + +def test_load_json_validationresult(): + json_data = r""" + { + "valid": true, + "errors": [] + } + """ + data = json.loads(json_data, strict=False) + instance = ValidationResult.load(data) + assert instance is not None + assert instance.valid + + +def test_load_yaml_validationresult(): + yaml_data = r""" + valid: true + errors: [] + + """ + data = yaml.load(yaml_data, Loader=yaml.FullLoader) + instance = ValidationResult.load(data) + assert instance is not None + assert instance.valid + + +def test_roundtrip_json_validationresult(): + """Test that load -> save -> load produces equivalent data.""" + json_data = r""" + { + "valid": true, + "errors": [] + } + """ + original_data = json.loads(json_data, strict=False) + instance = ValidationResult.load(original_data) + saved_data = instance.save() + reloaded = ValidationResult.load(saved_data) + assert reloaded is not None + assert reloaded.valid + + +def test_to_json_validationresult(): + """Test that to_json produces valid JSON.""" + json_data = r""" + { + "valid": true, + "errors": [] + } + """ + data = json.loads(json_data, strict=False) + instance = ValidationResult.load(data) + json_output = instance.to_json() + assert json_output is not None + parsed = json.loads(json_output) + assert isinstance(parsed, dict) + + +def test_to_yaml_validationresult(): + """Test that to_yaml produces valid YAML.""" + json_data = r""" + { + "valid": true, + "errors": [] + } + """ + data = json.loads(json_data, strict=False) + instance = ValidationResult.load(data) + yaml_output = instance.to_yaml() + assert yaml_output is not None + parsed = yaml.safe_load(yaml_output) + assert isinstance(parsed, dict) diff --git a/runtime/python/prompty/tests/model/streaming/test_stream_options.py b/runtime/python/prompty/tests/model/streaming/test_stream_options.py new file mode 100644 index 00000000..fafe09e7 --- /dev/null +++ b/runtime/python/prompty/tests/model/streaming/test_stream_options.py @@ -0,0 +1,73 @@ +import json + +import yaml + +from prompty.model import StreamOptions + + +def test_load_json_streamoptions(): + json_data = r""" + { + "includeUsage": true + } + """ + data = json.loads(json_data, strict=False) + instance = StreamOptions.load(data) + assert instance is not None + assert instance.include_usage + + +def test_load_yaml_streamoptions(): + yaml_data = r""" + includeUsage: true + + """ + data = yaml.load(yaml_data, Loader=yaml.FullLoader) + instance = StreamOptions.load(data) + assert instance is not None + assert instance.include_usage + + +def test_roundtrip_json_streamoptions(): + """Test that load -> save -> load produces equivalent data.""" + json_data = r""" + { + "includeUsage": true + } + """ + original_data = json.loads(json_data, strict=False) + instance = StreamOptions.load(original_data) + saved_data = instance.save() + reloaded = StreamOptions.load(saved_data) + assert reloaded is not None + assert reloaded.include_usage + + +def test_to_json_streamoptions(): + """Test that to_json produces valid JSON.""" + json_data = r""" + { + "includeUsage": true + } + """ + data = json.loads(json_data, strict=False) + instance = StreamOptions.load(data) + json_output = instance.to_json() + assert json_output is not None + parsed = json.loads(json_output) + assert isinstance(parsed, dict) + + +def test_to_yaml_streamoptions(): + """Test that to_yaml produces valid YAML.""" + json_data = r""" + { + "includeUsage": true + } + """ + data = json.loads(json_data, strict=False) + instance = StreamOptions.load(data) + yaml_output = instance.to_yaml() + assert yaml_output is not None + parsed = yaml.safe_load(yaml_output) + assert isinstance(parsed, dict) diff --git a/runtime/python/prompty/tests/model/tracing/test_trace_file.py b/runtime/python/prompty/tests/model/tracing/test_trace_file.py new file mode 100644 index 00000000..44da1474 --- /dev/null +++ b/runtime/python/prompty/tests/model/tracing/test_trace_file.py @@ -0,0 +1,81 @@ +import json + +import yaml + +from prompty.model import TraceFile + + +def test_load_json_tracefile(): + json_data = r""" + { + "runtime": "python", + "version": "2.0.0" + } + """ + data = json.loads(json_data, strict=False) + instance = TraceFile.load(data) + assert instance is not None + assert instance.runtime == "python" + assert instance.version == "2.0.0" + + +def test_load_yaml_tracefile(): + yaml_data = r""" + runtime: python + version: 2.0.0 + + """ + data = yaml.load(yaml_data, Loader=yaml.FullLoader) + instance = TraceFile.load(data) + assert instance is not None + assert instance.runtime == "python" + assert instance.version == "2.0.0" + + +def test_roundtrip_json_tracefile(): + """Test that load -> save -> load produces equivalent data.""" + json_data = r""" + { + "runtime": "python", + "version": "2.0.0" + } + """ + original_data = json.loads(json_data, strict=False) + instance = TraceFile.load(original_data) + saved_data = instance.save() + reloaded = TraceFile.load(saved_data) + assert reloaded is not None + assert reloaded.runtime == "python" + assert reloaded.version == "2.0.0" + + +def test_to_json_tracefile(): + """Test that to_json produces valid JSON.""" + json_data = r""" + { + "runtime": "python", + "version": "2.0.0" + } + """ + data = json.loads(json_data, strict=False) + instance = TraceFile.load(data) + json_output = instance.to_json() + assert json_output is not None + parsed = json.loads(json_output) + assert isinstance(parsed, dict) + + +def test_to_yaml_tracefile(): + """Test that to_yaml produces valid YAML.""" + json_data = r""" + { + "runtime": "python", + "version": "2.0.0" + } + """ + data = json.loads(json_data, strict=False) + instance = TraceFile.load(data) + yaml_output = instance.to_yaml() + assert yaml_output is not None + parsed = yaml.safe_load(yaml_output) + assert isinstance(parsed, dict) diff --git a/runtime/python/prompty/tests/model/tracing/test_trace_span.py b/runtime/python/prompty/tests/model/tracing/test_trace_span.py new file mode 100644 index 00000000..c0bf9fcb --- /dev/null +++ b/runtime/python/prompty/tests/model/tracing/test_trace_span.py @@ -0,0 +1,89 @@ +import json + +import yaml + +from prompty.model import TraceSpan + + +def test_load_json_tracespan(): + json_data = r""" + { + "name": "prompty.core.pipeline.run", + "signature": "prompty.core.pipeline.run", + "error": "Connection refused" + } + """ + data = json.loads(json_data, strict=False) + instance = TraceSpan.load(data) + assert instance is not None + assert instance.name == "prompty.core.pipeline.run" + assert instance.signature == "prompty.core.pipeline.run" + assert instance.error == "Connection refused" + + +def test_load_yaml_tracespan(): + yaml_data = r""" + name: prompty.core.pipeline.run + signature: prompty.core.pipeline.run + error: Connection refused + + """ + data = yaml.load(yaml_data, Loader=yaml.FullLoader) + instance = TraceSpan.load(data) + assert instance is not None + assert instance.name == "prompty.core.pipeline.run" + assert instance.signature == "prompty.core.pipeline.run" + assert instance.error == "Connection refused" + + +def test_roundtrip_json_tracespan(): + """Test that load -> save -> load produces equivalent data.""" + json_data = r""" + { + "name": "prompty.core.pipeline.run", + "signature": "prompty.core.pipeline.run", + "error": "Connection refused" + } + """ + original_data = json.loads(json_data, strict=False) + instance = TraceSpan.load(original_data) + saved_data = instance.save() + reloaded = TraceSpan.load(saved_data) + assert reloaded is not None + assert reloaded.name == "prompty.core.pipeline.run" + assert reloaded.signature == "prompty.core.pipeline.run" + assert reloaded.error == "Connection refused" + + +def test_to_json_tracespan(): + """Test that to_json produces valid JSON.""" + json_data = r""" + { + "name": "prompty.core.pipeline.run", + "signature": "prompty.core.pipeline.run", + "error": "Connection refused" + } + """ + data = json.loads(json_data, strict=False) + instance = TraceSpan.load(data) + json_output = instance.to_json() + assert json_output is not None + parsed = json.loads(json_output) + assert isinstance(parsed, dict) + + +def test_to_yaml_tracespan(): + """Test that to_yaml produces valid YAML.""" + json_data = r""" + { + "name": "prompty.core.pipeline.run", + "signature": "prompty.core.pipeline.run", + "error": "Connection refused" + } + """ + data = json.loads(json_data, strict=False) + instance = TraceSpan.load(data) + yaml_output = instance.to_yaml() + assert yaml_output is not None + parsed = yaml.safe_load(yaml_output) + assert isinstance(parsed, dict) diff --git a/runtime/python/prompty/tests/model/tracing/test_trace_time.py b/runtime/python/prompty/tests/model/tracing/test_trace_time.py new file mode 100644 index 00000000..4b49e7ba --- /dev/null +++ b/runtime/python/prompty/tests/model/tracing/test_trace_time.py @@ -0,0 +1,89 @@ +import json + +import yaml + +from prompty.model import TraceTime + + +def test_load_json_tracetime(): + json_data = r""" + { + "start": "2026-04-04T12:00:00Z", + "end": "2026-04-04T12:00:01Z", + "duration": 1000 + } + """ + data = json.loads(json_data, strict=False) + instance = TraceTime.load(data) + assert instance is not None + assert instance.start == "2026-04-04T12:00:00Z" + assert instance.end == "2026-04-04T12:00:01Z" + assert instance.duration == 1000 + + +def test_load_yaml_tracetime(): + yaml_data = r""" + start: "2026-04-04T12:00:00Z" + end: "2026-04-04T12:00:01Z" + duration: 1000 + + """ + data = yaml.load(yaml_data, Loader=yaml.FullLoader) + instance = TraceTime.load(data) + assert instance is not None + assert instance.start == "2026-04-04T12:00:00Z" + assert instance.end == "2026-04-04T12:00:01Z" + assert instance.duration == 1000 + + +def test_roundtrip_json_tracetime(): + """Test that load -> save -> load produces equivalent data.""" + json_data = r""" + { + "start": "2026-04-04T12:00:00Z", + "end": "2026-04-04T12:00:01Z", + "duration": 1000 + } + """ + original_data = json.loads(json_data, strict=False) + instance = TraceTime.load(original_data) + saved_data = instance.save() + reloaded = TraceTime.load(saved_data) + assert reloaded is not None + assert reloaded.start == "2026-04-04T12:00:00Z" + assert reloaded.end == "2026-04-04T12:00:01Z" + assert reloaded.duration == 1000 + + +def test_to_json_tracetime(): + """Test that to_json produces valid JSON.""" + json_data = r""" + { + "start": "2026-04-04T12:00:00Z", + "end": "2026-04-04T12:00:01Z", + "duration": 1000 + } + """ + data = json.loads(json_data, strict=False) + instance = TraceTime.load(data) + json_output = instance.to_json() + assert json_output is not None + parsed = json.loads(json_output) + assert isinstance(parsed, dict) + + +def test_to_yaml_tracetime(): + """Test that to_yaml produces valid YAML.""" + json_data = r""" + { + "start": "2026-04-04T12:00:00Z", + "end": "2026-04-04T12:00:01Z", + "duration": 1000 + } + """ + data = json.loads(json_data, strict=False) + instance = TraceTime.load(data) + yaml_output = instance.to_yaml() + assert yaml_output is not None + parsed = yaml.safe_load(yaml_output) + assert isinstance(parsed, dict) diff --git a/runtime/python/prompty/tests/model/wire/test_anthropic_image_block.py b/runtime/python/prompty/tests/model/wire/test_anthropic_image_block.py new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/runtime/python/prompty/tests/model/wire/test_anthropic_image_block.py @@ -0,0 +1 @@ + diff --git a/runtime/python/prompty/tests/model/wire/test_anthropic_image_source.py b/runtime/python/prompty/tests/model/wire/test_anthropic_image_source.py new file mode 100644 index 00000000..41b4b1b8 --- /dev/null +++ b/runtime/python/prompty/tests/model/wire/test_anthropic_image_source.py @@ -0,0 +1,81 @@ +import json + +import yaml + +from prompty.model import AnthropicImageSource + + +def test_load_json_anthropicimagesource(): + json_data = r""" + { + "media_type": "image/png", + "data": "iVBORw0KGgo..." + } + """ + data = json.loads(json_data, strict=False) + instance = AnthropicImageSource.load(data) + assert instance is not None + assert instance.media_type == "image/png" + assert instance.data == "iVBORw0KGgo..." + + +def test_load_yaml_anthropicimagesource(): + yaml_data = r""" + media_type: image/png + data: iVBORw0KGgo... + + """ + data = yaml.load(yaml_data, Loader=yaml.FullLoader) + instance = AnthropicImageSource.load(data) + assert instance is not None + assert instance.media_type == "image/png" + assert instance.data == "iVBORw0KGgo..." + + +def test_roundtrip_json_anthropicimagesource(): + """Test that load -> save -> load produces equivalent data.""" + json_data = r""" + { + "media_type": "image/png", + "data": "iVBORw0KGgo..." + } + """ + original_data = json.loads(json_data, strict=False) + instance = AnthropicImageSource.load(original_data) + saved_data = instance.save() + reloaded = AnthropicImageSource.load(saved_data) + assert reloaded is not None + assert reloaded.media_type == "image/png" + assert reloaded.data == "iVBORw0KGgo..." + + +def test_to_json_anthropicimagesource(): + """Test that to_json produces valid JSON.""" + json_data = r""" + { + "media_type": "image/png", + "data": "iVBORw0KGgo..." + } + """ + data = json.loads(json_data, strict=False) + instance = AnthropicImageSource.load(data) + json_output = instance.to_json() + assert json_output is not None + parsed = json.loads(json_output) + assert isinstance(parsed, dict) + + +def test_to_yaml_anthropicimagesource(): + """Test that to_yaml produces valid YAML.""" + json_data = r""" + { + "media_type": "image/png", + "data": "iVBORw0KGgo..." + } + """ + data = json.loads(json_data, strict=False) + instance = AnthropicImageSource.load(data) + yaml_output = instance.to_yaml() + assert yaml_output is not None + parsed = yaml.safe_load(yaml_output) + assert isinstance(parsed, dict) diff --git a/runtime/python/prompty/tests/model/wire/test_anthropic_messages_request.py b/runtime/python/prompty/tests/model/wire/test_anthropic_messages_request.py new file mode 100644 index 00000000..a785109a --- /dev/null +++ b/runtime/python/prompty/tests/model/wire/test_anthropic_messages_request.py @@ -0,0 +1,127 @@ +import json + +import yaml + +from prompty.model import AnthropicMessagesRequest + + +def test_load_json_anthropicmessagesrequest(): + json_data = r""" + { + "model": "claude-sonnet-4-20250514", + "max_tokens": 4096, + "system": "You are a helpful assistant.", + "temperature": 0.7, + "top_p": 0.9, + "top_k": 40, + "stop_sequences": [ + "\n\nHuman:" + ] + } + """ + data = json.loads(json_data, strict=False) + instance = AnthropicMessagesRequest.load(data) + assert instance is not None + assert instance.model == "claude-sonnet-4-20250514" + assert instance.max_tokens == 4096 + assert instance.system == "You are a helpful assistant." + assert instance.temperature == 0.7 + assert instance.top_p == 0.9 + assert instance.top_k == 40 + + +def test_load_yaml_anthropicmessagesrequest(): + yaml_data = r""" + model: claude-sonnet-4-20250514 + max_tokens: 4096 + system: You are a helpful assistant. + temperature: 0.7 + top_p: 0.9 + top_k: 40 + stop_sequences: + - "\n\nHuman:" + + """ + data = yaml.load(yaml_data, Loader=yaml.FullLoader) + instance = AnthropicMessagesRequest.load(data) + assert instance is not None + assert instance.model == "claude-sonnet-4-20250514" + assert instance.max_tokens == 4096 + assert instance.system == "You are a helpful assistant." + assert instance.temperature == 0.7 + assert instance.top_p == 0.9 + assert instance.top_k == 40 + + +def test_roundtrip_json_anthropicmessagesrequest(): + """Test that load -> save -> load produces equivalent data.""" + json_data = r""" + { + "model": "claude-sonnet-4-20250514", + "max_tokens": 4096, + "system": "You are a helpful assistant.", + "temperature": 0.7, + "top_p": 0.9, + "top_k": 40, + "stop_sequences": [ + "\n\nHuman:" + ] + } + """ + original_data = json.loads(json_data, strict=False) + instance = AnthropicMessagesRequest.load(original_data) + saved_data = instance.save() + reloaded = AnthropicMessagesRequest.load(saved_data) + assert reloaded is not None + assert reloaded.model == "claude-sonnet-4-20250514" + assert reloaded.max_tokens == 4096 + assert reloaded.system == "You are a helpful assistant." + assert reloaded.temperature == 0.7 + assert reloaded.top_p == 0.9 + assert reloaded.top_k == 40 + + +def test_to_json_anthropicmessagesrequest(): + """Test that to_json produces valid JSON.""" + json_data = r""" + { + "model": "claude-sonnet-4-20250514", + "max_tokens": 4096, + "system": "You are a helpful assistant.", + "temperature": 0.7, + "top_p": 0.9, + "top_k": 40, + "stop_sequences": [ + "\n\nHuman:" + ] + } + """ + data = json.loads(json_data, strict=False) + instance = AnthropicMessagesRequest.load(data) + json_output = instance.to_json() + assert json_output is not None + parsed = json.loads(json_output) + assert isinstance(parsed, dict) + + +def test_to_yaml_anthropicmessagesrequest(): + """Test that to_yaml produces valid YAML.""" + json_data = r""" + { + "model": "claude-sonnet-4-20250514", + "max_tokens": 4096, + "system": "You are a helpful assistant.", + "temperature": 0.7, + "top_p": 0.9, + "top_k": 40, + "stop_sequences": [ + "\n\nHuman:" + ] + } + """ + data = json.loads(json_data, strict=False) + instance = AnthropicMessagesRequest.load(data) + yaml_output = instance.to_yaml() + assert yaml_output is not None + parsed = yaml.safe_load(yaml_output) + assert isinstance(parsed, dict) diff --git a/runtime/python/prompty/tests/model/wire/test_anthropic_messages_response.py b/runtime/python/prompty/tests/model/wire/test_anthropic_messages_response.py new file mode 100644 index 00000000..40f3d812 --- /dev/null +++ b/runtime/python/prompty/tests/model/wire/test_anthropic_messages_response.py @@ -0,0 +1,89 @@ +import json + +import yaml + +from prompty.model import AnthropicMessagesResponse + + +def test_load_json_anthropicmessagesresponse(): + json_data = r""" + { + "id": "msg_01XFDUDYJgAACzvnptvVoYEL", + "model": "claude-sonnet-4-20250514", + "stop_reason": "end_turn" + } + """ + data = json.loads(json_data, strict=False) + instance = AnthropicMessagesResponse.load(data) + assert instance is not None + assert instance.id == "msg_01XFDUDYJgAACzvnptvVoYEL" + assert instance.model == "claude-sonnet-4-20250514" + assert instance.stop_reason == "end_turn" + + +def test_load_yaml_anthropicmessagesresponse(): + yaml_data = r""" + id: msg_01XFDUDYJgAACzvnptvVoYEL + model: claude-sonnet-4-20250514 + stop_reason: end_turn + + """ + data = yaml.load(yaml_data, Loader=yaml.FullLoader) + instance = AnthropicMessagesResponse.load(data) + assert instance is not None + assert instance.id == "msg_01XFDUDYJgAACzvnptvVoYEL" + assert instance.model == "claude-sonnet-4-20250514" + assert instance.stop_reason == "end_turn" + + +def test_roundtrip_json_anthropicmessagesresponse(): + """Test that load -> save -> load produces equivalent data.""" + json_data = r""" + { + "id": "msg_01XFDUDYJgAACzvnptvVoYEL", + "model": "claude-sonnet-4-20250514", + "stop_reason": "end_turn" + } + """ + original_data = json.loads(json_data, strict=False) + instance = AnthropicMessagesResponse.load(original_data) + saved_data = instance.save() + reloaded = AnthropicMessagesResponse.load(saved_data) + assert reloaded is not None + assert reloaded.id == "msg_01XFDUDYJgAACzvnptvVoYEL" + assert reloaded.model == "claude-sonnet-4-20250514" + assert reloaded.stop_reason == "end_turn" + + +def test_to_json_anthropicmessagesresponse(): + """Test that to_json produces valid JSON.""" + json_data = r""" + { + "id": "msg_01XFDUDYJgAACzvnptvVoYEL", + "model": "claude-sonnet-4-20250514", + "stop_reason": "end_turn" + } + """ + data = json.loads(json_data, strict=False) + instance = AnthropicMessagesResponse.load(data) + json_output = instance.to_json() + assert json_output is not None + parsed = json.loads(json_output) + assert isinstance(parsed, dict) + + +def test_to_yaml_anthropicmessagesresponse(): + """Test that to_yaml produces valid YAML.""" + json_data = r""" + { + "id": "msg_01XFDUDYJgAACzvnptvVoYEL", + "model": "claude-sonnet-4-20250514", + "stop_reason": "end_turn" + } + """ + data = json.loads(json_data, strict=False) + instance = AnthropicMessagesResponse.load(data) + yaml_output = instance.to_yaml() + assert yaml_output is not None + parsed = yaml.safe_load(yaml_output) + assert isinstance(parsed, dict) diff --git a/runtime/python/prompty/tests/model/wire/test_anthropic_text_block.py b/runtime/python/prompty/tests/model/wire/test_anthropic_text_block.py new file mode 100644 index 00000000..0cfa01a9 --- /dev/null +++ b/runtime/python/prompty/tests/model/wire/test_anthropic_text_block.py @@ -0,0 +1,73 @@ +import json + +import yaml + +from prompty.model import AnthropicTextBlock + + +def test_load_json_anthropictextblock(): + json_data = r""" + { + "text": "Hello, how can I help?" + } + """ + data = json.loads(json_data, strict=False) + instance = AnthropicTextBlock.load(data) + assert instance is not None + assert instance.text == "Hello, how can I help?" + + +def test_load_yaml_anthropictextblock(): + yaml_data = r""" + text: Hello, how can I help? + + """ + data = yaml.load(yaml_data, Loader=yaml.FullLoader) + instance = AnthropicTextBlock.load(data) + assert instance is not None + assert instance.text == "Hello, how can I help?" + + +def test_roundtrip_json_anthropictextblock(): + """Test that load -> save -> load produces equivalent data.""" + json_data = r""" + { + "text": "Hello, how can I help?" + } + """ + original_data = json.loads(json_data, strict=False) + instance = AnthropicTextBlock.load(original_data) + saved_data = instance.save() + reloaded = AnthropicTextBlock.load(saved_data) + assert reloaded is not None + assert reloaded.text == "Hello, how can I help?" + + +def test_to_json_anthropictextblock(): + """Test that to_json produces valid JSON.""" + json_data = r""" + { + "text": "Hello, how can I help?" + } + """ + data = json.loads(json_data, strict=False) + instance = AnthropicTextBlock.load(data) + json_output = instance.to_json() + assert json_output is not None + parsed = json.loads(json_output) + assert isinstance(parsed, dict) + + +def test_to_yaml_anthropictextblock(): + """Test that to_yaml produces valid YAML.""" + json_data = r""" + { + "text": "Hello, how can I help?" + } + """ + data = json.loads(json_data, strict=False) + instance = AnthropicTextBlock.load(data) + yaml_output = instance.to_yaml() + assert yaml_output is not None + parsed = yaml.safe_load(yaml_output) + assert isinstance(parsed, dict) diff --git a/runtime/python/prompty/tests/model/wire/test_anthropic_tool_definition.py b/runtime/python/prompty/tests/model/wire/test_anthropic_tool_definition.py new file mode 100644 index 00000000..9567cfb8 --- /dev/null +++ b/runtime/python/prompty/tests/model/wire/test_anthropic_tool_definition.py @@ -0,0 +1,81 @@ +import json + +import yaml + +from prompty.model import AnthropicToolDefinition + + +def test_load_json_anthropictooldefinition(): + json_data = r""" + { + "name": "get_weather", + "description": "Get the current weather for a city" + } + """ + data = json.loads(json_data, strict=False) + instance = AnthropicToolDefinition.load(data) + assert instance is not None + assert instance.name == "get_weather" + assert instance.description == "Get the current weather for a city" + + +def test_load_yaml_anthropictooldefinition(): + yaml_data = r""" + name: get_weather + description: Get the current weather for a city + + """ + data = yaml.load(yaml_data, Loader=yaml.FullLoader) + instance = AnthropicToolDefinition.load(data) + assert instance is not None + assert instance.name == "get_weather" + assert instance.description == "Get the current weather for a city" + + +def test_roundtrip_json_anthropictooldefinition(): + """Test that load -> save -> load produces equivalent data.""" + json_data = r""" + { + "name": "get_weather", + "description": "Get the current weather for a city" + } + """ + original_data = json.loads(json_data, strict=False) + instance = AnthropicToolDefinition.load(original_data) + saved_data = instance.save() + reloaded = AnthropicToolDefinition.load(saved_data) + assert reloaded is not None + assert reloaded.name == "get_weather" + assert reloaded.description == "Get the current weather for a city" + + +def test_to_json_anthropictooldefinition(): + """Test that to_json produces valid JSON.""" + json_data = r""" + { + "name": "get_weather", + "description": "Get the current weather for a city" + } + """ + data = json.loads(json_data, strict=False) + instance = AnthropicToolDefinition.load(data) + json_output = instance.to_json() + assert json_output is not None + parsed = json.loads(json_output) + assert isinstance(parsed, dict) + + +def test_to_yaml_anthropictooldefinition(): + """Test that to_yaml produces valid YAML.""" + json_data = r""" + { + "name": "get_weather", + "description": "Get the current weather for a city" + } + """ + data = json.loads(json_data, strict=False) + instance = AnthropicToolDefinition.load(data) + yaml_output = instance.to_yaml() + assert yaml_output is not None + parsed = yaml.safe_load(yaml_output) + assert isinstance(parsed, dict) diff --git a/runtime/python/prompty/tests/model/wire/test_anthropic_tool_result_block.py b/runtime/python/prompty/tests/model/wire/test_anthropic_tool_result_block.py new file mode 100644 index 00000000..a2d244d2 --- /dev/null +++ b/runtime/python/prompty/tests/model/wire/test_anthropic_tool_result_block.py @@ -0,0 +1,81 @@ +import json + +import yaml + +from prompty.model import AnthropicToolResultBlock + + +def test_load_json_anthropictoolresultblock(): + json_data = r""" + { + "tool_use_id": "toolu_01A09q90qw90lq917835lq9", + "content": "72°F and sunny in Paris" + } + """ + data = json.loads(json_data, strict=False) + instance = AnthropicToolResultBlock.load(data) + assert instance is not None + assert instance.tool_use_id == "toolu_01A09q90qw90lq917835lq9" + assert instance.content == "72°F and sunny in Paris" + + +def test_load_yaml_anthropictoolresultblock(): + yaml_data = r""" + tool_use_id: toolu_01A09q90qw90lq917835lq9 + content: 72°F and sunny in Paris + + """ + data = yaml.load(yaml_data, Loader=yaml.FullLoader) + instance = AnthropicToolResultBlock.load(data) + assert instance is not None + assert instance.tool_use_id == "toolu_01A09q90qw90lq917835lq9" + assert instance.content == "72°F and sunny in Paris" + + +def test_roundtrip_json_anthropictoolresultblock(): + """Test that load -> save -> load produces equivalent data.""" + json_data = r""" + { + "tool_use_id": "toolu_01A09q90qw90lq917835lq9", + "content": "72°F and sunny in Paris" + } + """ + original_data = json.loads(json_data, strict=False) + instance = AnthropicToolResultBlock.load(original_data) + saved_data = instance.save() + reloaded = AnthropicToolResultBlock.load(saved_data) + assert reloaded is not None + assert reloaded.tool_use_id == "toolu_01A09q90qw90lq917835lq9" + assert reloaded.content == "72°F and sunny in Paris" + + +def test_to_json_anthropictoolresultblock(): + """Test that to_json produces valid JSON.""" + json_data = r""" + { + "tool_use_id": "toolu_01A09q90qw90lq917835lq9", + "content": "72°F and sunny in Paris" + } + """ + data = json.loads(json_data, strict=False) + instance = AnthropicToolResultBlock.load(data) + json_output = instance.to_json() + assert json_output is not None + parsed = json.loads(json_output) + assert isinstance(parsed, dict) + + +def test_to_yaml_anthropictoolresultblock(): + """Test that to_yaml produces valid YAML.""" + json_data = r""" + { + "tool_use_id": "toolu_01A09q90qw90lq917835lq9", + "content": "72°F and sunny in Paris" + } + """ + data = json.loads(json_data, strict=False) + instance = AnthropicToolResultBlock.load(data) + yaml_output = instance.to_yaml() + assert yaml_output is not None + parsed = yaml.safe_load(yaml_output) + assert isinstance(parsed, dict) diff --git a/runtime/python/prompty/tests/model/wire/test_anthropic_tool_use_block.py b/runtime/python/prompty/tests/model/wire/test_anthropic_tool_use_block.py new file mode 100644 index 00000000..933bbac6 --- /dev/null +++ b/runtime/python/prompty/tests/model/wire/test_anthropic_tool_use_block.py @@ -0,0 +1,95 @@ +import json + +import yaml + +from prompty.model import AnthropicToolUseBlock + + +def test_load_json_anthropictooluseblock(): + json_data = r""" + { + "id": "toolu_01A09q90qw90lq917835lq9", + "name": "get_weather", + "input": { + "city": "Paris" + } + } + """ + data = json.loads(json_data, strict=False) + instance = AnthropicToolUseBlock.load(data) + assert instance is not None + assert instance.id == "toolu_01A09q90qw90lq917835lq9" + assert instance.name == "get_weather" + + +def test_load_yaml_anthropictooluseblock(): + yaml_data = r""" + id: toolu_01A09q90qw90lq917835lq9 + name: get_weather + input: + city: Paris + + """ + data = yaml.load(yaml_data, Loader=yaml.FullLoader) + instance = AnthropicToolUseBlock.load(data) + assert instance is not None + assert instance.id == "toolu_01A09q90qw90lq917835lq9" + assert instance.name == "get_weather" + + +def test_roundtrip_json_anthropictooluseblock(): + """Test that load -> save -> load produces equivalent data.""" + json_data = r""" + { + "id": "toolu_01A09q90qw90lq917835lq9", + "name": "get_weather", + "input": { + "city": "Paris" + } + } + """ + original_data = json.loads(json_data, strict=False) + instance = AnthropicToolUseBlock.load(original_data) + saved_data = instance.save() + reloaded = AnthropicToolUseBlock.load(saved_data) + assert reloaded is not None + assert reloaded.id == "toolu_01A09q90qw90lq917835lq9" + assert reloaded.name == "get_weather" + + +def test_to_json_anthropictooluseblock(): + """Test that to_json produces valid JSON.""" + json_data = r""" + { + "id": "toolu_01A09q90qw90lq917835lq9", + "name": "get_weather", + "input": { + "city": "Paris" + } + } + """ + data = json.loads(json_data, strict=False) + instance = AnthropicToolUseBlock.load(data) + json_output = instance.to_json() + assert json_output is not None + parsed = json.loads(json_output) + assert isinstance(parsed, dict) + + +def test_to_yaml_anthropictooluseblock(): + """Test that to_yaml produces valid YAML.""" + json_data = r""" + { + "id": "toolu_01A09q90qw90lq917835lq9", + "name": "get_weather", + "input": { + "city": "Paris" + } + } + """ + data = json.loads(json_data, strict=False) + instance = AnthropicToolUseBlock.load(data) + yaml_output = instance.to_yaml() + assert yaml_output is not None + parsed = yaml.safe_load(yaml_output) + assert isinstance(parsed, dict) diff --git a/runtime/python/prompty/tests/model/wire/test_anthropic_usage.py b/runtime/python/prompty/tests/model/wire/test_anthropic_usage.py new file mode 100644 index 00000000..b117e7e8 --- /dev/null +++ b/runtime/python/prompty/tests/model/wire/test_anthropic_usage.py @@ -0,0 +1,81 @@ +import json + +import yaml + +from prompty.model import AnthropicUsage + + +def test_load_json_anthropicusage(): + json_data = r""" + { + "input_tokens": 150, + "output_tokens": 42 + } + """ + data = json.loads(json_data, strict=False) + instance = AnthropicUsage.load(data) + assert instance is not None + assert instance.input_tokens == 150 + assert instance.output_tokens == 42 + + +def test_load_yaml_anthropicusage(): + yaml_data = r""" + input_tokens: 150 + output_tokens: 42 + + """ + data = yaml.load(yaml_data, Loader=yaml.FullLoader) + instance = AnthropicUsage.load(data) + assert instance is not None + assert instance.input_tokens == 150 + assert instance.output_tokens == 42 + + +def test_roundtrip_json_anthropicusage(): + """Test that load -> save -> load produces equivalent data.""" + json_data = r""" + { + "input_tokens": 150, + "output_tokens": 42 + } + """ + original_data = json.loads(json_data, strict=False) + instance = AnthropicUsage.load(original_data) + saved_data = instance.save() + reloaded = AnthropicUsage.load(saved_data) + assert reloaded is not None + assert reloaded.input_tokens == 150 + assert reloaded.output_tokens == 42 + + +def test_to_json_anthropicusage(): + """Test that to_json produces valid JSON.""" + json_data = r""" + { + "input_tokens": 150, + "output_tokens": 42 + } + """ + data = json.loads(json_data, strict=False) + instance = AnthropicUsage.load(data) + json_output = instance.to_json() + assert json_output is not None + parsed = json.loads(json_output) + assert isinstance(parsed, dict) + + +def test_to_yaml_anthropicusage(): + """Test that to_yaml produces valid YAML.""" + json_data = r""" + { + "input_tokens": 150, + "output_tokens": 42 + } + """ + data = json.loads(json_data, strict=False) + instance = AnthropicUsage.load(data) + yaml_output = instance.to_yaml() + assert yaml_output is not None + parsed = yaml.safe_load(yaml_output) + assert isinstance(parsed, dict) diff --git a/runtime/python/prompty/tests/model/wire/test_anthropic_wire_message.py b/runtime/python/prompty/tests/model/wire/test_anthropic_wire_message.py new file mode 100644 index 00000000..463b607d --- /dev/null +++ b/runtime/python/prompty/tests/model/wire/test_anthropic_wire_message.py @@ -0,0 +1,73 @@ +import json + +import yaml + +from prompty.model import AnthropicWireMessage + + +def test_load_json_anthropicwiremessage(): + json_data = r""" + { + "role": "user" + } + """ + data = json.loads(json_data, strict=False) + instance = AnthropicWireMessage.load(data) + assert instance is not None + assert instance.role == "user" + + +def test_load_yaml_anthropicwiremessage(): + yaml_data = r""" + role: user + + """ + data = yaml.load(yaml_data, Loader=yaml.FullLoader) + instance = AnthropicWireMessage.load(data) + assert instance is not None + assert instance.role == "user" + + +def test_roundtrip_json_anthropicwiremessage(): + """Test that load -> save -> load produces equivalent data.""" + json_data = r""" + { + "role": "user" + } + """ + original_data = json.loads(json_data, strict=False) + instance = AnthropicWireMessage.load(original_data) + saved_data = instance.save() + reloaded = AnthropicWireMessage.load(saved_data) + assert reloaded is not None + assert reloaded.role == "user" + + +def test_to_json_anthropicwiremessage(): + """Test that to_json produces valid JSON.""" + json_data = r""" + { + "role": "user" + } + """ + data = json.loads(json_data, strict=False) + instance = AnthropicWireMessage.load(data) + json_output = instance.to_json() + assert json_output is not None + parsed = json.loads(json_output) + assert isinstance(parsed, dict) + + +def test_to_yaml_anthropicwiremessage(): + """Test that to_yaml produces valid YAML.""" + json_data = r""" + { + "role": "user" + } + """ + data = json.loads(json_data, strict=False) + instance = AnthropicWireMessage.load(data) + yaml_output = instance.to_yaml() + assert yaml_output is not None + parsed = yaml.safe_load(yaml_output) + assert isinstance(parsed, dict) diff --git a/runtime/rust/prompty/src/model/core/file_not_found_error.rs b/runtime/rust/prompty/src/model/core/file_not_found_error.rs new file mode 100644 index 00000000..e0935073 --- /dev/null +++ b/runtime/rust/prompty/src/model/core/file_not_found_error.rs @@ -0,0 +1,69 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +use super::super::context::{LoadContext, SaveContext}; + +/// Raised when a referenced file cannot be found. This applies to both .prompty files and ${file:path} references in frontmatter. +#[derive(Debug, Clone, Default)] +pub struct FileNotFoundError { + /// Human-readable error message + pub message: String, + /// The file path that could not be resolved + pub path: String, +} + +impl FileNotFoundError { + /// Create a new FileNotFoundError with default values. + pub fn new() -> Self { + Self::default() + } + + /// Load FileNotFoundError from a JSON string. + pub fn from_json(json: &str, ctx: &LoadContext) -> Result { + let value: serde_json::Value = serde_json::from_str(json)?; + Ok(Self::load_from_value(&value, ctx)) + } + + /// Load FileNotFoundError from a YAML string. + pub fn from_yaml(yaml: &str, ctx: &LoadContext) -> Result { + let value: serde_json::Value = serde_yaml::from_str(yaml)?; + Ok(Self::load_from_value(&value, ctx)) + } + + /// Load FileNotFoundError from a `serde_json::Value`. + /// + /// Calls `ctx.process_input` before field extraction. + pub fn load_from_value(value: &serde_json::Value, ctx: &LoadContext) -> Self { + let value = ctx.process_input(value.clone()); + Self { + message: value.get("message").and_then(|v| v.as_str()).unwrap_or_default().to_string(), + path: value.get("path").and_then(|v| v.as_str()).unwrap_or_default().to_string(), + } + } + + /// Serialize FileNotFoundError to a `serde_json::Value`. + /// + /// Calls `ctx.process_dict` after serialization. + pub fn to_value(&self, ctx: &SaveContext) -> serde_json::Value { + let mut result = serde_json::Map::new(); + // Write base fields + if !self.message.is_empty() { + result.insert("message".to_string(), serde_json::Value::String(self.message.clone())); + } + if !self.path.is_empty() { + result.insert("path".to_string(), serde_json::Value::String(self.path.clone())); + } + ctx.process_dict(serde_json::Value::Object(result)) + } + + /// Serialize FileNotFoundError to a JSON string. + pub fn to_json(&self, ctx: &SaveContext) -> Result { + serde_json::to_string_pretty(&self.to_value(ctx)) + } + + /// Serialize FileNotFoundError to a YAML string. + pub fn to_yaml(&self, ctx: &SaveContext) -> Result { + serde_yaml::to_string(&self.to_value(ctx)) + } +} diff --git a/runtime/rust/prompty/src/model/core/invoker_error.rs b/runtime/rust/prompty/src/model/core/invoker_error.rs new file mode 100644 index 00000000..baf3417e --- /dev/null +++ b/runtime/rust/prompty/src/model/core/invoker_error.rs @@ -0,0 +1,75 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +use super::super::context::{LoadContext, SaveContext}; + +/// Raised when no invoker implementation is registered for a given component and key. For example, if no renderer is registered for the key "jinja2", an InvokerError is raised. +#[derive(Debug, Clone, Default)] +pub struct InvokerError { + /// Human-readable error message + pub message: String, + /// The pipeline component type that was being looked up (e.g., 'renderer', 'parser', 'executor', 'processor') + pub component: String, + /// The registration key that was not found + pub key: String, +} + +impl InvokerError { + /// Create a new InvokerError with default values. + pub fn new() -> Self { + Self::default() + } + + /// Load InvokerError from a JSON string. + pub fn from_json(json: &str, ctx: &LoadContext) -> Result { + let value: serde_json::Value = serde_json::from_str(json)?; + Ok(Self::load_from_value(&value, ctx)) + } + + /// Load InvokerError from a YAML string. + pub fn from_yaml(yaml: &str, ctx: &LoadContext) -> Result { + let value: serde_json::Value = serde_yaml::from_str(yaml)?; + Ok(Self::load_from_value(&value, ctx)) + } + + /// Load InvokerError from a `serde_json::Value`. + /// + /// Calls `ctx.process_input` before field extraction. + pub fn load_from_value(value: &serde_json::Value, ctx: &LoadContext) -> Self { + let value = ctx.process_input(value.clone()); + Self { + message: value.get("message").and_then(|v| v.as_str()).unwrap_or_default().to_string(), + component: value.get("component").and_then(|v| v.as_str()).unwrap_or_default().to_string(), + key: value.get("key").and_then(|v| v.as_str()).unwrap_or_default().to_string(), + } + } + + /// Serialize InvokerError to a `serde_json::Value`. + /// + /// Calls `ctx.process_dict` after serialization. + pub fn to_value(&self, ctx: &SaveContext) -> serde_json::Value { + let mut result = serde_json::Map::new(); + // Write base fields + if !self.message.is_empty() { + result.insert("message".to_string(), serde_json::Value::String(self.message.clone())); + } + if !self.component.is_empty() { + result.insert("component".to_string(), serde_json::Value::String(self.component.clone())); + } + if !self.key.is_empty() { + result.insert("key".to_string(), serde_json::Value::String(self.key.clone())); + } + ctx.process_dict(serde_json::Value::Object(result)) + } + + /// Serialize InvokerError to a JSON string. + pub fn to_json(&self, ctx: &SaveContext) -> Result { + serde_json::to_string_pretty(&self.to_value(ctx)) + } + + /// Serialize InvokerError to a YAML string. + pub fn to_yaml(&self, ctx: &SaveContext) -> Result { + serde_yaml::to_string(&self.to_value(ctx)) + } +} diff --git a/runtime/rust/prompty/src/model/core/validation_error.rs b/runtime/rust/prompty/src/model/core/validation_error.rs new file mode 100644 index 00000000..f7212eae --- /dev/null +++ b/runtime/rust/prompty/src/model/core/validation_error.rs @@ -0,0 +1,75 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +use super::super::context::{LoadContext, SaveContext}; + +/// Raised when input validation fails. Each ValidationError describes a single property that did not satisfy its constraint. +#[derive(Debug, Clone, Default)] +pub struct ValidationError { + /// Human-readable error message + pub message: String, + /// The name of the property that failed validation + pub property: String, + /// The constraint that was violated (e.g., 'required', 'type') + pub constraint: String, +} + +impl ValidationError { + /// Create a new ValidationError with default values. + pub fn new() -> Self { + Self::default() + } + + /// Load ValidationError from a JSON string. + pub fn from_json(json: &str, ctx: &LoadContext) -> Result { + let value: serde_json::Value = serde_json::from_str(json)?; + Ok(Self::load_from_value(&value, ctx)) + } + + /// Load ValidationError from a YAML string. + pub fn from_yaml(yaml: &str, ctx: &LoadContext) -> Result { + let value: serde_json::Value = serde_yaml::from_str(yaml)?; + Ok(Self::load_from_value(&value, ctx)) + } + + /// Load ValidationError from a `serde_json::Value`. + /// + /// Calls `ctx.process_input` before field extraction. + pub fn load_from_value(value: &serde_json::Value, ctx: &LoadContext) -> Self { + let value = ctx.process_input(value.clone()); + Self { + message: value.get("message").and_then(|v| v.as_str()).unwrap_or_default().to_string(), + property: value.get("property").and_then(|v| v.as_str()).unwrap_or_default().to_string(), + constraint: value.get("constraint").and_then(|v| v.as_str()).unwrap_or_default().to_string(), + } + } + + /// Serialize ValidationError to a `serde_json::Value`. + /// + /// Calls `ctx.process_dict` after serialization. + pub fn to_value(&self, ctx: &SaveContext) -> serde_json::Value { + let mut result = serde_json::Map::new(); + // Write base fields + if !self.message.is_empty() { + result.insert("message".to_string(), serde_json::Value::String(self.message.clone())); + } + if !self.property.is_empty() { + result.insert("property".to_string(), serde_json::Value::String(self.property.clone())); + } + if !self.constraint.is_empty() { + result.insert("constraint".to_string(), serde_json::Value::String(self.constraint.clone())); + } + ctx.process_dict(serde_json::Value::Object(result)) + } + + /// Serialize ValidationError to a JSON string. + pub fn to_json(&self, ctx: &SaveContext) -> Result { + serde_json::to_string_pretty(&self.to_value(ctx)) + } + + /// Serialize ValidationError to a YAML string. + pub fn to_yaml(&self, ctx: &SaveContext) -> Result { + serde_yaml::to_string(&self.to_value(ctx)) + } +} diff --git a/runtime/rust/prompty/src/model/core/validation_result.rs b/runtime/rust/prompty/src/model/core/validation_result.rs new file mode 100644 index 00000000..23f3f54e --- /dev/null +++ b/runtime/rust/prompty/src/model/core/validation_result.rs @@ -0,0 +1,89 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +use super::super::context::{LoadContext, SaveContext}; + +use super::validation_error::ValidationError; + +/// The result of validating inputs against an agent's inputSchema. Returned by `validate_inputs` (§12.2) to indicate whether all required inputs are present and satisfy their constraints. +#[derive(Debug, Clone, Default)] +pub struct ValidationResult { + /// Whether all inputs passed validation + pub valid: bool, + /// List of validation errors (empty when valid is true) + pub errors: Vec, +} + +impl ValidationResult { + /// Create a new ValidationResult with default values. + pub fn new() -> Self { + Self::default() + } + + /// Load ValidationResult from a JSON string. + pub fn from_json(json: &str, ctx: &LoadContext) -> Result { + let value: serde_json::Value = serde_json::from_str(json)?; + Ok(Self::load_from_value(&value, ctx)) + } + + /// Load ValidationResult from a YAML string. + pub fn from_yaml(yaml: &str, ctx: &LoadContext) -> Result { + let value: serde_json::Value = serde_yaml::from_str(yaml)?; + Ok(Self::load_from_value(&value, ctx)) + } + + /// Load ValidationResult from a `serde_json::Value`. + /// + /// Calls `ctx.process_input` before field extraction. + pub fn load_from_value(value: &serde_json::Value, ctx: &LoadContext) -> Self { + let value = ctx.process_input(value.clone()); + Self { + valid: value.get("valid").and_then(|v| v.as_bool()).unwrap_or(false), + errors: value.get("errors").map(|v| Self::load_errors(v, ctx)).unwrap_or_default(), + } + } + + /// Serialize ValidationResult to a `serde_json::Value`. + /// + /// Calls `ctx.process_dict` after serialization. + pub fn to_value(&self, ctx: &SaveContext) -> serde_json::Value { + let mut result = serde_json::Map::new(); + // Write base fields + result.insert("valid".to_string(), serde_json::Value::Bool(self.valid)); + if !self.errors.is_empty() { + result.insert("errors".to_string(), Self::save_errors(&self.errors, ctx)); + } + ctx.process_dict(serde_json::Value::Object(result)) + } + + /// Serialize ValidationResult to a JSON string. + pub fn to_json(&self, ctx: &SaveContext) -> Result { + serde_json::to_string_pretty(&self.to_value(ctx)) + } + + /// Serialize ValidationResult to a YAML string. + pub fn to_yaml(&self, ctx: &SaveContext) -> Result { + serde_yaml::to_string(&self.to_value(ctx)) + } + + /// Load a collection of ValidationError from a JSON value. + /// Handles both array format `[{...}]`. + fn load_errors(data: &serde_json::Value, ctx: &LoadContext) -> Vec { + match data { + serde_json::Value::Array(arr) => { + arr.iter().map(|v| ValidationError::load_from_value(v, ctx)).collect() + } + + _ => Vec::new(), + + } + } + + /// Save a collection of ValidationError to a JSON value. + fn save_errors(items: &[ValidationError], ctx: &SaveContext) -> serde_json::Value { + + serde_json::Value::Array(items.iter().map(|item| item.to_value(ctx)).collect::>()) + + } +} diff --git a/runtime/rust/prompty/src/model/streaming/mod.rs b/runtime/rust/prompty/src/model/streaming/mod.rs new file mode 100644 index 00000000..7ef9b667 --- /dev/null +++ b/runtime/rust/prompty/src/model/streaming/mod.rs @@ -0,0 +1,6 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +pub mod stream_options; +pub use stream_options::*; diff --git a/runtime/rust/prompty/src/model/streaming/stream_options.rs b/runtime/rust/prompty/src/model/streaming/stream_options.rs new file mode 100644 index 00000000..f32fc1d4 --- /dev/null +++ b/runtime/rust/prompty/src/model/streaming/stream_options.rs @@ -0,0 +1,63 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +use super::super::context::{LoadContext, SaveContext}; + +/// Options controlling streaming behavior for LLM API calls. Passed alongside the model options when streaming is enabled. +#[derive(Debug, Clone, Default)] +pub struct StreamOptions { + /// When true, the final streaming chunk includes token usage statistics + pub include_usage: Option, +} + +impl StreamOptions { + /// Create a new StreamOptions with default values. + pub fn new() -> Self { + Self::default() + } + + /// Load StreamOptions from a JSON string. + pub fn from_json(json: &str, ctx: &LoadContext) -> Result { + let value: serde_json::Value = serde_json::from_str(json)?; + Ok(Self::load_from_value(&value, ctx)) + } + + /// Load StreamOptions from a YAML string. + pub fn from_yaml(yaml: &str, ctx: &LoadContext) -> Result { + let value: serde_json::Value = serde_yaml::from_str(yaml)?; + Ok(Self::load_from_value(&value, ctx)) + } + + /// Load StreamOptions from a `serde_json::Value`. + /// + /// Calls `ctx.process_input` before field extraction. + pub fn load_from_value(value: &serde_json::Value, ctx: &LoadContext) -> Self { + let value = ctx.process_input(value.clone()); + Self { + include_usage: value.get("includeUsage").and_then(|v| v.as_bool()), + } + } + + /// Serialize StreamOptions to a `serde_json::Value`. + /// + /// Calls `ctx.process_dict` after serialization. + pub fn to_value(&self, ctx: &SaveContext) -> serde_json::Value { + let mut result = serde_json::Map::new(); + // Write base fields + if let Some(val) = self.include_usage { + result.insert("includeUsage".to_string(), serde_json::Value::Bool(val)); + } + ctx.process_dict(serde_json::Value::Object(result)) + } + + /// Serialize StreamOptions to a JSON string. + pub fn to_json(&self, ctx: &SaveContext) -> Result { + serde_json::to_string_pretty(&self.to_value(ctx)) + } + + /// Serialize StreamOptions to a YAML string. + pub fn to_yaml(&self, ctx: &SaveContext) -> Result { + serde_yaml::to_string(&self.to_value(ctx)) + } +} diff --git a/runtime/rust/prompty/src/model/tracing/mod.rs b/runtime/rust/prompty/src/model/tracing/mod.rs new file mode 100644 index 00000000..265458be --- /dev/null +++ b/runtime/rust/prompty/src/model/tracing/mod.rs @@ -0,0 +1,12 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +pub mod trace_time; +pub use trace_time::*; + +pub mod trace_span; +pub use trace_span::*; + +pub mod trace_file; +pub use trace_file::*; diff --git a/runtime/rust/prompty/src/model/tracing/trace_file.rs b/runtime/rust/prompty/src/model/tracing/trace_file.rs new file mode 100644 index 00000000..6bf9244c --- /dev/null +++ b/runtime/rust/prompty/src/model/tracing/trace_file.rs @@ -0,0 +1,80 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +use super::super::context::{LoadContext, SaveContext}; + +use super::trace_span::TraceSpan; + +/// The top-level .tracy file structure written by the file backend (§3.6.1). +#[derive(Debug, Clone, Default)] +pub struct TraceFile { + /// Language/runtime name (e.g., 'python', 'csharp', 'javascript') + pub runtime: String, + /// Prompty library version + pub version: String, + /// The root trace span + pub trace: TraceSpan, +} + +impl TraceFile { + /// Create a new TraceFile with default values. + pub fn new() -> Self { + Self::default() + } + + /// Load TraceFile from a JSON string. + pub fn from_json(json: &str, ctx: &LoadContext) -> Result { + let value: serde_json::Value = serde_json::from_str(json)?; + Ok(Self::load_from_value(&value, ctx)) + } + + /// Load TraceFile from a YAML string. + pub fn from_yaml(yaml: &str, ctx: &LoadContext) -> Result { + let value: serde_json::Value = serde_yaml::from_str(yaml)?; + Ok(Self::load_from_value(&value, ctx)) + } + + /// Load TraceFile from a `serde_json::Value`. + /// + /// Calls `ctx.process_input` before field extraction. + pub fn load_from_value(value: &serde_json::Value, ctx: &LoadContext) -> Self { + let value = ctx.process_input(value.clone()); + Self { + runtime: value.get("runtime").and_then(|v| v.as_str()).unwrap_or_default().to_string(), + version: value.get("version").and_then(|v| v.as_str()).unwrap_or_default().to_string(), + trace: value.get("trace").filter(|v| v.is_object() || v.is_array() || v.is_string()).map(|v| TraceSpan::load_from_value(v, ctx)).unwrap_or_default(), + } + } + + /// Serialize TraceFile to a `serde_json::Value`. + /// + /// Calls `ctx.process_dict` after serialization. + pub fn to_value(&self, ctx: &SaveContext) -> serde_json::Value { + let mut result = serde_json::Map::new(); + // Write base fields + if !self.runtime.is_empty() { + result.insert("runtime".to_string(), serde_json::Value::String(self.runtime.clone())); + } + if !self.version.is_empty() { + result.insert("version".to_string(), serde_json::Value::String(self.version.clone())); + } + { + let nested = self.trace.to_value(ctx); + if !nested.is_null() { + result.insert("trace".to_string(), nested); + } + } + ctx.process_dict(serde_json::Value::Object(result)) + } + + /// Serialize TraceFile to a JSON string. + pub fn to_json(&self, ctx: &SaveContext) -> Result { + serde_json::to_string_pretty(&self.to_value(ctx)) + } + + /// Serialize TraceFile to a YAML string. + pub fn to_yaml(&self, ctx: &SaveContext) -> Result { + serde_yaml::to_string(&self.to_value(ctx)) + } +} diff --git a/runtime/rust/prompty/src/model/tracing/trace_span.rs b/runtime/rust/prompty/src/model/tracing/trace_span.rs new file mode 100644 index 00000000..c115a582 --- /dev/null +++ b/runtime/rust/prompty/src/model/tracing/trace_span.rs @@ -0,0 +1,133 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +use super::super::context::{LoadContext, SaveContext}; + +use super::super::model::token_usage::TokenUsage; + +use super::trace_time::TraceTime; + +/// A single trace span capturing one pipeline stage or function invocation. Spans nest via the `__frames` field to form a tree representing the full execution (§3.6.1). +#[derive(Debug, Clone, Default)] +pub struct TraceSpan { + /// The name of this span (typically the function signature) + pub name: String, + /// Timing information for this span + pub __time: TraceTime, + /// Fully-qualified function signature that produced this span + pub signature: Option, + /// Serialized input parameters (redacted per §3.4) + pub inputs: serde_json::Value, + /// Serialized return value or error information (redacted per §3.4) + pub output: Option, + /// Error message if the span ended with an exception + pub error: Option, + /// Aggregated token usage hoisted from child spans (§3.5) + pub __usage: Option, + /// Additional span attributes (e.g., OpenTelemetry GenAI attributes) + pub attributes: serde_json::Value, + /// Nested child spans forming the execution tree (recursive; each element is a TraceSpan) + pub __frames: Option>, +} + +impl TraceSpan { + /// Create a new TraceSpan with default values. + pub fn new() -> Self { + Self::default() + } + + /// Load TraceSpan from a JSON string. + pub fn from_json(json: &str, ctx: &LoadContext) -> Result { + let value: serde_json::Value = serde_json::from_str(json)?; + Ok(Self::load_from_value(&value, ctx)) + } + + /// Load TraceSpan from a YAML string. + pub fn from_yaml(yaml: &str, ctx: &LoadContext) -> Result { + let value: serde_json::Value = serde_yaml::from_str(yaml)?; + Ok(Self::load_from_value(&value, ctx)) + } + + /// Load TraceSpan from a `serde_json::Value`. + /// + /// Calls `ctx.process_input` before field extraction. + pub fn load_from_value(value: &serde_json::Value, ctx: &LoadContext) -> Self { + let value = ctx.process_input(value.clone()); + Self { + name: value.get("name").and_then(|v| v.as_str()).unwrap_or_default().to_string(), + __time: value.get("__time").filter(|v| v.is_object() || v.is_array() || v.is_string()).map(|v| TraceTime::load_from_value(v, ctx)).unwrap_or_default(), + signature: value.get("signature").and_then(|v| v.as_str()).map(|s| s.to_string()), + inputs: value.get("inputs").cloned().unwrap_or(serde_json::Value::Null), + output: value.get("output").cloned(), + error: value.get("error").and_then(|v| v.as_str()).map(|s| s.to_string()), + __usage: value.get("__usage").filter(|v| v.is_object() || v.is_array() || v.is_string()).map(|v| TokenUsage::load_from_value(v, ctx)), + attributes: value.get("attributes").cloned().unwrap_or(serde_json::Value::Null), + __frames: value.get("__frames").and_then(|v| v.as_array()).map(|arr| arr.to_vec()), + } + } + + /// Serialize TraceSpan to a `serde_json::Value`. + /// + /// Calls `ctx.process_dict` after serialization. + pub fn to_value(&self, ctx: &SaveContext) -> serde_json::Value { + let mut result = serde_json::Map::new(); + // Write base fields + if !self.name.is_empty() { + result.insert("name".to_string(), serde_json::Value::String(self.name.clone())); + } + { + let nested = self.__time.to_value(ctx); + if !nested.is_null() { + result.insert("__time".to_string(), nested); + } + } + if let Some(ref val) = self.signature { + result.insert("signature".to_string(), serde_json::Value::String(val.clone())); + } + if !self.inputs.is_null() { + result.insert("inputs".to_string(), self.inputs.clone()); + } + if let Some(ref val) = self.output { + result.insert("output".to_string(), val.clone()); + } + if let Some(ref val) = self.error { + result.insert("error".to_string(), serde_json::Value::String(val.clone())); + } + if let Some(ref val) = self.__usage { + let nested = val.to_value(ctx); + if !nested.is_null() { + result.insert("__usage".to_string(), nested); + } + } + if !self.attributes.is_null() { + result.insert("attributes".to_string(), self.attributes.clone()); + } + if let Some(ref items) = self.__frames { + result.insert("__frames".to_string(), serde_json::to_value(items).unwrap_or(serde_json::Value::Null)); + } + ctx.process_dict(serde_json::Value::Object(result)) + } + + /// Serialize TraceSpan to a JSON string. + pub fn to_json(&self, ctx: &SaveContext) -> Result { + serde_json::to_string_pretty(&self.to_value(ctx)) + } + + /// Serialize TraceSpan to a YAML string. + pub fn to_yaml(&self, ctx: &SaveContext) -> Result { + serde_yaml::to_string(&self.to_value(ctx)) + } + /// Returns typed reference to the map if the field is an object. + /// Returns `None` if the field is null or not an object. + pub fn as_inputs_dict(&self) -> Option<&serde_json::Map> { + self.inputs.as_object() + } + + /// Returns typed reference to the map if the field is an object. + /// Returns `None` if the field is null or not an object. + pub fn as_attributes_dict(&self) -> Option<&serde_json::Map> { + self.attributes.as_object() + } + +} diff --git a/runtime/rust/prompty/src/model/tracing/trace_time.rs b/runtime/rust/prompty/src/model/tracing/trace_time.rs new file mode 100644 index 00000000..23fb1393 --- /dev/null +++ b/runtime/rust/prompty/src/model/tracing/trace_time.rs @@ -0,0 +1,75 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +use super::super::context::{LoadContext, SaveContext}; + +/// Timing information for a trace span. +#[derive(Debug, Clone, Default)] +pub struct TraceTime { + /// ISO 8601 UTC timestamp when the span started + pub start: String, + /// ISO 8601 UTC timestamp when the span ended + pub end: String, + /// Duration of the span in milliseconds + pub duration: f64, +} + +impl TraceTime { + /// Create a new TraceTime with default values. + pub fn new() -> Self { + Self::default() + } + + /// Load TraceTime from a JSON string. + pub fn from_json(json: &str, ctx: &LoadContext) -> Result { + let value: serde_json::Value = serde_json::from_str(json)?; + Ok(Self::load_from_value(&value, ctx)) + } + + /// Load TraceTime from a YAML string. + pub fn from_yaml(yaml: &str, ctx: &LoadContext) -> Result { + let value: serde_json::Value = serde_yaml::from_str(yaml)?; + Ok(Self::load_from_value(&value, ctx)) + } + + /// Load TraceTime from a `serde_json::Value`. + /// + /// Calls `ctx.process_input` before field extraction. + pub fn load_from_value(value: &serde_json::Value, ctx: &LoadContext) -> Self { + let value = ctx.process_input(value.clone()); + Self { + start: value.get("start").and_then(|v| v.as_str()).unwrap_or_default().to_string(), + end: value.get("end").and_then(|v| v.as_str()).unwrap_or_default().to_string(), + duration: value.get("duration").and_then(|v| v.as_f64()).unwrap_or(0.0), + } + } + + /// Serialize TraceTime to a `serde_json::Value`. + /// + /// Calls `ctx.process_dict` after serialization. + pub fn to_value(&self, ctx: &SaveContext) -> serde_json::Value { + let mut result = serde_json::Map::new(); + // Write base fields + if !self.start.is_empty() { + result.insert("start".to_string(), serde_json::Value::String(self.start.clone())); + } + if !self.end.is_empty() { + result.insert("end".to_string(), serde_json::Value::String(self.end.clone())); + } + if self.duration != 0.0 { + result.insert("duration".to_string(), serde_json::Number::from_f64(self.duration as f64).map(serde_json::Value::Number).unwrap_or(serde_json::Value::Null)); + } + ctx.process_dict(serde_json::Value::Object(result)) + } + + /// Serialize TraceTime to a JSON string. + pub fn to_json(&self, ctx: &SaveContext) -> Result { + serde_json::to_string_pretty(&self.to_value(ctx)) + } + + /// Serialize TraceTime to a YAML string. + pub fn to_yaml(&self, ctx: &SaveContext) -> Result { + serde_yaml::to_string(&self.to_value(ctx)) + } +} diff --git a/runtime/rust/prompty/src/model/wire/anthropic_image_block.rs b/runtime/rust/prompty/src/model/wire/anthropic_image_block.rs new file mode 100644 index 00000000..1aea8a32 --- /dev/null +++ b/runtime/rust/prompty/src/model/wire/anthropic_image_block.rs @@ -0,0 +1,74 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +use super::super::context::{LoadContext, SaveContext}; + +use super::anthropic_image_source::AnthropicImageSource; + +/// An image content block using base64-encoded data. Anthropic requires images as base64 with an explicit media type. +#[derive(Debug, Clone, Default)] +pub struct AnthropicImageBlock { + /// The content block type + pub type: String, + /// The image source (base64-encoded) + pub source: AnthropicImageSource, +} + +impl AnthropicImageBlock { + /// Create a new AnthropicImageBlock with default values. + pub fn new() -> Self { + Self::default() + } + + /// Load AnthropicImageBlock from a JSON string. + pub fn from_json(json: &str, ctx: &LoadContext) -> Result { + let value: serde_json::Value = serde_json::from_str(json)?; + Ok(Self::load_from_value(&value, ctx)) + } + + /// Load AnthropicImageBlock from a YAML string. + pub fn from_yaml(yaml: &str, ctx: &LoadContext) -> Result { + let value: serde_json::Value = serde_yaml::from_str(yaml)?; + Ok(Self::load_from_value(&value, ctx)) + } + + /// Load AnthropicImageBlock from a `serde_json::Value`. + /// + /// Calls `ctx.process_input` before field extraction. + pub fn load_from_value(value: &serde_json::Value, ctx: &LoadContext) -> Self { + let value = ctx.process_input(value.clone()); + Self { + type: value.get("type").and_then(|v| v.as_str()).unwrap_or_default().to_string(), + source: value.get("source").filter(|v| v.is_object() || v.is_array() || v.is_string()).map(|v| AnthropicImageSource::load_from_value(v, ctx)).unwrap_or_default(), + } + } + + /// Serialize AnthropicImageBlock to a `serde_json::Value`. + /// + /// Calls `ctx.process_dict` after serialization. + pub fn to_value(&self, ctx: &SaveContext) -> serde_json::Value { + let mut result = serde_json::Map::new(); + // Write base fields + if !self.type.is_empty() { + result.insert("type".to_string(), serde_json::Value::String(self.type.clone())); + } + { + let nested = self.source.to_value(ctx); + if !nested.is_null() { + result.insert("source".to_string(), nested); + } + } + ctx.process_dict(serde_json::Value::Object(result)) + } + + /// Serialize AnthropicImageBlock to a JSON string. + pub fn to_json(&self, ctx: &SaveContext) -> Result { + serde_json::to_string_pretty(&self.to_value(ctx)) + } + + /// Serialize AnthropicImageBlock to a YAML string. + pub fn to_yaml(&self, ctx: &SaveContext) -> Result { + serde_yaml::to_string(&self.to_value(ctx)) + } +} diff --git a/runtime/rust/prompty/src/model/wire/anthropic_image_source.rs b/runtime/rust/prompty/src/model/wire/anthropic_image_source.rs new file mode 100644 index 00000000..a78a4186 --- /dev/null +++ b/runtime/rust/prompty/src/model/wire/anthropic_image_source.rs @@ -0,0 +1,75 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +use super::super::context::{LoadContext, SaveContext}; + +/// Source descriptor for an Anthropic base64 image. +#[derive(Debug, Clone, Default)] +pub struct AnthropicImageSource { + /// The encoding type (always 'base64' for inline images) + pub type: String, + /// The MIME type of the image (e.g., 'image/png', 'image/jpeg') + pub media_type: String, + /// The base64-encoded image data + pub data: String, +} + +impl AnthropicImageSource { + /// Create a new AnthropicImageSource with default values. + pub fn new() -> Self { + Self::default() + } + + /// Load AnthropicImageSource from a JSON string. + pub fn from_json(json: &str, ctx: &LoadContext) -> Result { + let value: serde_json::Value = serde_json::from_str(json)?; + Ok(Self::load_from_value(&value, ctx)) + } + + /// Load AnthropicImageSource from a YAML string. + pub fn from_yaml(yaml: &str, ctx: &LoadContext) -> Result { + let value: serde_json::Value = serde_yaml::from_str(yaml)?; + Ok(Self::load_from_value(&value, ctx)) + } + + /// Load AnthropicImageSource from a `serde_json::Value`. + /// + /// Calls `ctx.process_input` before field extraction. + pub fn load_from_value(value: &serde_json::Value, ctx: &LoadContext) -> Self { + let value = ctx.process_input(value.clone()); + Self { + type: value.get("type").and_then(|v| v.as_str()).unwrap_or_default().to_string(), + media_type: value.get("media_type").and_then(|v| v.as_str()).unwrap_or_default().to_string(), + data: value.get("data").and_then(|v| v.as_str()).unwrap_or_default().to_string(), + } + } + + /// Serialize AnthropicImageSource to a `serde_json::Value`. + /// + /// Calls `ctx.process_dict` after serialization. + pub fn to_value(&self, ctx: &SaveContext) -> serde_json::Value { + let mut result = serde_json::Map::new(); + // Write base fields + if !self.type.is_empty() { + result.insert("type".to_string(), serde_json::Value::String(self.type.clone())); + } + if !self.media_type.is_empty() { + result.insert("media_type".to_string(), serde_json::Value::String(self.media_type.clone())); + } + if !self.data.is_empty() { + result.insert("data".to_string(), serde_json::Value::String(self.data.clone())); + } + ctx.process_dict(serde_json::Value::Object(result)) + } + + /// Serialize AnthropicImageSource to a JSON string. + pub fn to_json(&self, ctx: &SaveContext) -> Result { + serde_json::to_string_pretty(&self.to_value(ctx)) + } + + /// Serialize AnthropicImageSource to a YAML string. + pub fn to_yaml(&self, ctx: &SaveContext) -> Result { + serde_yaml::to_string(&self.to_value(ctx)) + } +} diff --git a/runtime/rust/prompty/src/model/wire/anthropic_messages_request.rs b/runtime/rust/prompty/src/model/wire/anthropic_messages_request.rs new file mode 100644 index 00000000..c73214cc --- /dev/null +++ b/runtime/rust/prompty/src/model/wire/anthropic_messages_request.rs @@ -0,0 +1,187 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +use super::super::context::{LoadContext, SaveContext}; + +use super::anthropic_tool_definition::AnthropicToolDefinition; + +use super::anthropic_wire_message::AnthropicWireMessage; + +/// The full request body for the Anthropic Messages API (§7.5). +#[derive(Debug, Clone, Default)] +pub struct AnthropicMessagesRequest { + /// The model identifier + pub model: String, + /// The non-system messages to send + pub messages: Vec, + /// Maximum number of tokens to generate (required by Anthropic) + pub max_tokens: i32, + /// System prompt text (extracted from system-role messages) + pub system: Option, + /// Sampling temperature + pub temperature: Option, + /// Top-P sampling value + pub top_p: Option, + /// Top-K sampling value + pub top_k: Option, + /// Stop sequences to end generation + pub stop_sequences: Option>, + /// Tool definitions available to the model + pub tools: Vec, +} + +impl AnthropicMessagesRequest { + /// Create a new AnthropicMessagesRequest with default values. + pub fn new() -> Self { + Self::default() + } + + /// Load AnthropicMessagesRequest from a JSON string. + pub fn from_json(json: &str, ctx: &LoadContext) -> Result { + let value: serde_json::Value = serde_json::from_str(json)?; + Ok(Self::load_from_value(&value, ctx)) + } + + /// Load AnthropicMessagesRequest from a YAML string. + pub fn from_yaml(yaml: &str, ctx: &LoadContext) -> Result { + let value: serde_json::Value = serde_yaml::from_str(yaml)?; + Ok(Self::load_from_value(&value, ctx)) + } + + /// Load AnthropicMessagesRequest from a `serde_json::Value`. + /// + /// Calls `ctx.process_input` before field extraction. + pub fn load_from_value(value: &serde_json::Value, ctx: &LoadContext) -> Self { + let value = ctx.process_input(value.clone()); + Self { + model: value.get("model").and_then(|v| v.as_str()).unwrap_or_default().to_string(), + messages: value.get("messages").map(|v| Self::load_messages(v, ctx)).unwrap_or_default(), + max_tokens: value.get("max_tokens").and_then(|v| v.as_i64()).unwrap_or(0) as i32, + system: value.get("system").and_then(|v| v.as_str()).map(|s| s.to_string()), + temperature: value.get("temperature").and_then(|v| v.as_f64()).map(|v| v as f32), + top_p: value.get("top_p").and_then(|v| v.as_f64()).map(|v| v as f32), + top_k: value.get("top_k").and_then(|v| v.as_i64()).map(|v| v as i32), + stop_sequences: value.get("stop_sequences").and_then(|v| v.as_array()).map(|arr| arr.iter().filter_map(|v| v.as_str().map(|s| s.to_string())).collect()), + tools: value.get("tools").map(|v| Self::load_tools(v, ctx)).unwrap_or_default(), + } + } + + /// Serialize AnthropicMessagesRequest to a `serde_json::Value`. + /// + /// Calls `ctx.process_dict` after serialization. + pub fn to_value(&self, ctx: &SaveContext) -> serde_json::Value { + let mut result = serde_json::Map::new(); + // Write base fields + if !self.model.is_empty() { + result.insert("model".to_string(), serde_json::Value::String(self.model.clone())); + } + if !self.messages.is_empty() { + result.insert("messages".to_string(), Self::save_messages(&self.messages, ctx)); + } + if self.max_tokens != 0 { + result.insert("max_tokens".to_string(), serde_json::Value::Number(serde_json::Number::from(self.max_tokens))); + } + if let Some(ref val) = self.system { + result.insert("system".to_string(), serde_json::Value::String(val.clone())); + } + if let Some(val) = self.temperature { + result.insert("temperature".to_string(), serde_json::Number::from_f64(val as f64).map(serde_json::Value::Number).unwrap_or(serde_json::Value::Null)); + } + if let Some(val) = self.top_p { + result.insert("top_p".to_string(), serde_json::Number::from_f64(val as f64).map(serde_json::Value::Number).unwrap_or(serde_json::Value::Null)); + } + if let Some(val) = self.top_k { + result.insert("top_k".to_string(), serde_json::Value::Number(serde_json::Number::from(val))); + } + if let Some(ref items) = self.stop_sequences { + result.insert("stop_sequences".to_string(), serde_json::to_value(items).unwrap_or(serde_json::Value::Null)); + } + if !self.tools.is_empty() { + result.insert("tools".to_string(), Self::save_tools(&self.tools, ctx)); + } + ctx.process_dict(serde_json::Value::Object(result)) + } + + /// Serialize AnthropicMessagesRequest to a JSON string. + pub fn to_json(&self, ctx: &SaveContext) -> Result { + serde_json::to_string_pretty(&self.to_value(ctx)) + } + + /// Serialize AnthropicMessagesRequest to a YAML string. + pub fn to_yaml(&self, ctx: &SaveContext) -> Result { + serde_yaml::to_string(&self.to_value(ctx)) + } + + /// Load a collection of AnthropicWireMessage from a JSON value. + /// Handles both array format `[{...}]`. + fn load_messages(data: &serde_json::Value, ctx: &LoadContext) -> Vec { + match data { + serde_json::Value::Array(arr) => { + arr.iter().map(|v| AnthropicWireMessage::load_from_value(v, ctx)).collect() + } + + _ => Vec::new(), + + } + } + + /// Save a collection of AnthropicWireMessage to a JSON value. + fn save_messages(items: &[AnthropicWireMessage], ctx: &SaveContext) -> serde_json::Value { + + serde_json::Value::Array(items.iter().map(|item| item.to_value(ctx)).collect::>()) + + } + + /// Load a collection of AnthropicToolDefinition from a JSON value. + /// Handles both array format `[{...}]` and dict format `{"name": {...}}`. + fn load_tools(data: &serde_json::Value, ctx: &LoadContext) -> Vec { + match data { + serde_json::Value::Array(arr) => { + arr.iter().map(|v| AnthropicToolDefinition::load_from_value(v, ctx)).collect() + } + + serde_json::Value::Object(obj) => { + obj.iter() + .filter_map(|(name, value)| { + if value.is_array() { + return None; + } + let mut v = if value.is_object() { + value.clone() + } else { + serde_json::json!({ "description": value }) + }; + if let serde_json::Value::Object(ref mut m) = v { + m.entry("name".to_string()).or_insert_with(|| serde_json::Value::String(name.clone())); + } + Some(AnthropicToolDefinition::load_from_value(&v, ctx)) + }) + .collect() + } + _ => Vec::new(), + + } + } + + /// Save a collection of AnthropicToolDefinition to a JSON value. + fn save_tools(items: &[AnthropicToolDefinition], ctx: &SaveContext) -> serde_json::Value { + + if ctx.collection_format == "array" { + return serde_json::Value::Array(items.iter().map(|item| item.to_value(ctx)).collect::>()); + } + // Object format: use name as key + let mut result = serde_json::Map::new(); + for item in items { + let mut item_data = match item.to_value(ctx) { + serde_json::Value::Object(m) => m, + other => { let mut m = serde_json::Map::new(); m.insert("value".to_string(), other); m }, + }; + if let Some(serde_json::Value::String(name)) = item_data.remove("name") { + result.insert(name, serde_json::Value::Object(item_data)); + } + } + serde_json::Value::Object(result) + + } +} diff --git a/runtime/rust/prompty/src/model/wire/anthropic_messages_response.rs b/runtime/rust/prompty/src/model/wire/anthropic_messages_response.rs new file mode 100644 index 00000000..45d13f97 --- /dev/null +++ b/runtime/rust/prompty/src/model/wire/anthropic_messages_response.rs @@ -0,0 +1,104 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +use super::super::context::{LoadContext, SaveContext}; + +use super::anthropic_usage::AnthropicUsage; + +/// The response body from the Anthropic Messages API. +#[derive(Debug, Clone, Default)] +pub struct AnthropicMessagesResponse { + /// Unique response identifier + pub id: String, + /// Object type (always 'message') + pub type: String, + /// The role of the response (always 'assistant') + pub role: String, + /// Array of content blocks in the response (AnthropicTextBlock | AnthropicToolUseBlock) + pub content: Vec, + /// The model that generated the response + pub model: String, + /// The reason generation stopped ('end_turn', 'max_tokens', 'stop_sequence', 'tool_use') + pub stop_reason: String, + /// Token usage statistics + pub usage: AnthropicUsage, +} + +impl AnthropicMessagesResponse { + /// Create a new AnthropicMessagesResponse with default values. + pub fn new() -> Self { + Self::default() + } + + /// Load AnthropicMessagesResponse from a JSON string. + pub fn from_json(json: &str, ctx: &LoadContext) -> Result { + let value: serde_json::Value = serde_json::from_str(json)?; + Ok(Self::load_from_value(&value, ctx)) + } + + /// Load AnthropicMessagesResponse from a YAML string. + pub fn from_yaml(yaml: &str, ctx: &LoadContext) -> Result { + let value: serde_json::Value = serde_yaml::from_str(yaml)?; + Ok(Self::load_from_value(&value, ctx)) + } + + /// Load AnthropicMessagesResponse from a `serde_json::Value`. + /// + /// Calls `ctx.process_input` before field extraction. + pub fn load_from_value(value: &serde_json::Value, ctx: &LoadContext) -> Self { + let value = ctx.process_input(value.clone()); + Self { + id: value.get("id").and_then(|v| v.as_str()).unwrap_or_default().to_string(), + type: value.get("type").and_then(|v| v.as_str()).unwrap_or_default().to_string(), + role: value.get("role").and_then(|v| v.as_str()).unwrap_or_default().to_string(), + content: value.get("content").and_then(|v| v.as_array()).map(|arr| arr.to_vec()).unwrap_or_default(), + model: value.get("model").and_then(|v| v.as_str()).unwrap_or_default().to_string(), + stop_reason: value.get("stop_reason").and_then(|v| v.as_str()).unwrap_or_default().to_string(), + usage: value.get("usage").filter(|v| v.is_object() || v.is_array() || v.is_string()).map(|v| AnthropicUsage::load_from_value(v, ctx)).unwrap_or_default(), + } + } + + /// Serialize AnthropicMessagesResponse to a `serde_json::Value`. + /// + /// Calls `ctx.process_dict` after serialization. + pub fn to_value(&self, ctx: &SaveContext) -> serde_json::Value { + let mut result = serde_json::Map::new(); + // Write base fields + if !self.id.is_empty() { + result.insert("id".to_string(), serde_json::Value::String(self.id.clone())); + } + if !self.type.is_empty() { + result.insert("type".to_string(), serde_json::Value::String(self.type.clone())); + } + if !self.role.is_empty() { + result.insert("role".to_string(), serde_json::Value::String(self.role.clone())); + } + if !self.content.is_empty() { + result.insert("content".to_string(), serde_json::to_value(&self.content).unwrap_or(serde_json::Value::Null)); + } + if !self.model.is_empty() { + result.insert("model".to_string(), serde_json::Value::String(self.model.clone())); + } + if !self.stop_reason.is_empty() { + result.insert("stop_reason".to_string(), serde_json::Value::String(self.stop_reason.clone())); + } + { + let nested = self.usage.to_value(ctx); + if !nested.is_null() { + result.insert("usage".to_string(), nested); + } + } + ctx.process_dict(serde_json::Value::Object(result)) + } + + /// Serialize AnthropicMessagesResponse to a JSON string. + pub fn to_json(&self, ctx: &SaveContext) -> Result { + serde_json::to_string_pretty(&self.to_value(ctx)) + } + + /// Serialize AnthropicMessagesResponse to a YAML string. + pub fn to_yaml(&self, ctx: &SaveContext) -> Result { + serde_yaml::to_string(&self.to_value(ctx)) + } +} diff --git a/runtime/rust/prompty/src/model/wire/anthropic_text_block.rs b/runtime/rust/prompty/src/model/wire/anthropic_text_block.rs new file mode 100644 index 00000000..83bb8b2c --- /dev/null +++ b/runtime/rust/prompty/src/model/wire/anthropic_text_block.rs @@ -0,0 +1,69 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +use super::super::context::{LoadContext, SaveContext}; + +/// A text content block in Anthropic's array-of-blocks message format. +#[derive(Debug, Clone, Default)] +pub struct AnthropicTextBlock { + /// The content block type + pub type: String, + /// The text content + pub text: String, +} + +impl AnthropicTextBlock { + /// Create a new AnthropicTextBlock with default values. + pub fn new() -> Self { + Self::default() + } + + /// Load AnthropicTextBlock from a JSON string. + pub fn from_json(json: &str, ctx: &LoadContext) -> Result { + let value: serde_json::Value = serde_json::from_str(json)?; + Ok(Self::load_from_value(&value, ctx)) + } + + /// Load AnthropicTextBlock from a YAML string. + pub fn from_yaml(yaml: &str, ctx: &LoadContext) -> Result { + let value: serde_json::Value = serde_yaml::from_str(yaml)?; + Ok(Self::load_from_value(&value, ctx)) + } + + /// Load AnthropicTextBlock from a `serde_json::Value`. + /// + /// Calls `ctx.process_input` before field extraction. + pub fn load_from_value(value: &serde_json::Value, ctx: &LoadContext) -> Self { + let value = ctx.process_input(value.clone()); + Self { + type: value.get("type").and_then(|v| v.as_str()).unwrap_or_default().to_string(), + text: value.get("text").and_then(|v| v.as_str()).unwrap_or_default().to_string(), + } + } + + /// Serialize AnthropicTextBlock to a `serde_json::Value`. + /// + /// Calls `ctx.process_dict` after serialization. + pub fn to_value(&self, ctx: &SaveContext) -> serde_json::Value { + let mut result = serde_json::Map::new(); + // Write base fields + if !self.type.is_empty() { + result.insert("type".to_string(), serde_json::Value::String(self.type.clone())); + } + if !self.text.is_empty() { + result.insert("text".to_string(), serde_json::Value::String(self.text.clone())); + } + ctx.process_dict(serde_json::Value::Object(result)) + } + + /// Serialize AnthropicTextBlock to a JSON string. + pub fn to_json(&self, ctx: &SaveContext) -> Result { + serde_json::to_string_pretty(&self.to_value(ctx)) + } + + /// Serialize AnthropicTextBlock to a YAML string. + pub fn to_yaml(&self, ctx: &SaveContext) -> Result { + serde_yaml::to_string(&self.to_value(ctx)) + } +} diff --git a/runtime/rust/prompty/src/model/wire/anthropic_tool_definition.rs b/runtime/rust/prompty/src/model/wire/anthropic_tool_definition.rs new file mode 100644 index 00000000..366586f3 --- /dev/null +++ b/runtime/rust/prompty/src/model/wire/anthropic_tool_definition.rs @@ -0,0 +1,81 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +use super::super::context::{LoadContext, SaveContext}; + +/// A tool definition in Anthropic's format. Unlike OpenAI which wraps tools in `{type: "function", function: {...}}`, Anthropic uses a flat structure with `input_schema` (§7.5). +#[derive(Debug, Clone, Default)] +pub struct AnthropicToolDefinition { + /// The tool name + pub name: String, + /// A description of what the tool does + pub description: Option, + /// JSON Schema describing the tool's input parameters + pub input_schema: serde_json::Value, +} + +impl AnthropicToolDefinition { + /// Create a new AnthropicToolDefinition with default values. + pub fn new() -> Self { + Self::default() + } + + /// Load AnthropicToolDefinition from a JSON string. + pub fn from_json(json: &str, ctx: &LoadContext) -> Result { + let value: serde_json::Value = serde_json::from_str(json)?; + Ok(Self::load_from_value(&value, ctx)) + } + + /// Load AnthropicToolDefinition from a YAML string. + pub fn from_yaml(yaml: &str, ctx: &LoadContext) -> Result { + let value: serde_json::Value = serde_yaml::from_str(yaml)?; + Ok(Self::load_from_value(&value, ctx)) + } + + /// Load AnthropicToolDefinition from a `serde_json::Value`. + /// + /// Calls `ctx.process_input` before field extraction. + pub fn load_from_value(value: &serde_json::Value, ctx: &LoadContext) -> Self { + let value = ctx.process_input(value.clone()); + Self { + name: value.get("name").and_then(|v| v.as_str()).unwrap_or_default().to_string(), + description: value.get("description").and_then(|v| v.as_str()).map(|s| s.to_string()), + input_schema: value.get("input_schema").cloned().unwrap_or(serde_json::Value::Null), + } + } + + /// Serialize AnthropicToolDefinition to a `serde_json::Value`. + /// + /// Calls `ctx.process_dict` after serialization. + pub fn to_value(&self, ctx: &SaveContext) -> serde_json::Value { + let mut result = serde_json::Map::new(); + // Write base fields + if !self.name.is_empty() { + result.insert("name".to_string(), serde_json::Value::String(self.name.clone())); + } + if let Some(ref val) = self.description { + result.insert("description".to_string(), serde_json::Value::String(val.clone())); + } + if !self.input_schema.is_null() { + result.insert("input_schema".to_string(), self.input_schema.clone()); + } + ctx.process_dict(serde_json::Value::Object(result)) + } + + /// Serialize AnthropicToolDefinition to a JSON string. + pub fn to_json(&self, ctx: &SaveContext) -> Result { + serde_json::to_string_pretty(&self.to_value(ctx)) + } + + /// Serialize AnthropicToolDefinition to a YAML string. + pub fn to_yaml(&self, ctx: &SaveContext) -> Result { + serde_yaml::to_string(&self.to_value(ctx)) + } + /// Returns typed reference to the map if the field is an object. + /// Returns `None` if the field is null or not an object. + pub fn as_input_schema_dict(&self) -> Option<&serde_json::Map> { + self.input_schema.as_object() + } + +} diff --git a/runtime/rust/prompty/src/model/wire/anthropic_tool_result_block.rs b/runtime/rust/prompty/src/model/wire/anthropic_tool_result_block.rs new file mode 100644 index 00000000..7238b054 --- /dev/null +++ b/runtime/rust/prompty/src/model/wire/anthropic_tool_result_block.rs @@ -0,0 +1,75 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +use super::super::context::{LoadContext, SaveContext}; + +/// A tool result content block sent back to the API with the tool's output. +#[derive(Debug, Clone, Default)] +pub struct AnthropicToolResultBlock { + /// The content block type + pub type: String, + /// The tool_use id this result corresponds to + pub tool_use_id: String, + /// The tool's output content + pub content: String, +} + +impl AnthropicToolResultBlock { + /// Create a new AnthropicToolResultBlock with default values. + pub fn new() -> Self { + Self::default() + } + + /// Load AnthropicToolResultBlock from a JSON string. + pub fn from_json(json: &str, ctx: &LoadContext) -> Result { + let value: serde_json::Value = serde_json::from_str(json)?; + Ok(Self::load_from_value(&value, ctx)) + } + + /// Load AnthropicToolResultBlock from a YAML string. + pub fn from_yaml(yaml: &str, ctx: &LoadContext) -> Result { + let value: serde_json::Value = serde_yaml::from_str(yaml)?; + Ok(Self::load_from_value(&value, ctx)) + } + + /// Load AnthropicToolResultBlock from a `serde_json::Value`. + /// + /// Calls `ctx.process_input` before field extraction. + pub fn load_from_value(value: &serde_json::Value, ctx: &LoadContext) -> Self { + let value = ctx.process_input(value.clone()); + Self { + type: value.get("type").and_then(|v| v.as_str()).unwrap_or_default().to_string(), + tool_use_id: value.get("tool_use_id").and_then(|v| v.as_str()).unwrap_or_default().to_string(), + content: value.get("content").and_then(|v| v.as_str()).unwrap_or_default().to_string(), + } + } + + /// Serialize AnthropicToolResultBlock to a `serde_json::Value`. + /// + /// Calls `ctx.process_dict` after serialization. + pub fn to_value(&self, ctx: &SaveContext) -> serde_json::Value { + let mut result = serde_json::Map::new(); + // Write base fields + if !self.type.is_empty() { + result.insert("type".to_string(), serde_json::Value::String(self.type.clone())); + } + if !self.tool_use_id.is_empty() { + result.insert("tool_use_id".to_string(), serde_json::Value::String(self.tool_use_id.clone())); + } + if !self.content.is_empty() { + result.insert("content".to_string(), serde_json::Value::String(self.content.clone())); + } + ctx.process_dict(serde_json::Value::Object(result)) + } + + /// Serialize AnthropicToolResultBlock to a JSON string. + pub fn to_json(&self, ctx: &SaveContext) -> Result { + serde_json::to_string_pretty(&self.to_value(ctx)) + } + + /// Serialize AnthropicToolResultBlock to a YAML string. + pub fn to_yaml(&self, ctx: &SaveContext) -> Result { + serde_yaml::to_string(&self.to_value(ctx)) + } +} diff --git a/runtime/rust/prompty/src/model/wire/anthropic_tool_use_block.rs b/runtime/rust/prompty/src/model/wire/anthropic_tool_use_block.rs new file mode 100644 index 00000000..e7ef7f79 --- /dev/null +++ b/runtime/rust/prompty/src/model/wire/anthropic_tool_use_block.rs @@ -0,0 +1,87 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +use super::super::context::{LoadContext, SaveContext}; + +/// A tool use content block returned in an assistant message when the model wants to invoke a tool. +#[derive(Debug, Clone, Default)] +pub struct AnthropicToolUseBlock { + /// The content block type + pub type: String, + /// Unique identifier for this tool invocation + pub id: String, + /// The name of the tool to invoke + pub name: String, + /// The JSON arguments for the tool call + pub input: serde_json::Value, +} + +impl AnthropicToolUseBlock { + /// Create a new AnthropicToolUseBlock with default values. + pub fn new() -> Self { + Self::default() + } + + /// Load AnthropicToolUseBlock from a JSON string. + pub fn from_json(json: &str, ctx: &LoadContext) -> Result { + let value: serde_json::Value = serde_json::from_str(json)?; + Ok(Self::load_from_value(&value, ctx)) + } + + /// Load AnthropicToolUseBlock from a YAML string. + pub fn from_yaml(yaml: &str, ctx: &LoadContext) -> Result { + let value: serde_json::Value = serde_yaml::from_str(yaml)?; + Ok(Self::load_from_value(&value, ctx)) + } + + /// Load AnthropicToolUseBlock from a `serde_json::Value`. + /// + /// Calls `ctx.process_input` before field extraction. + pub fn load_from_value(value: &serde_json::Value, ctx: &LoadContext) -> Self { + let value = ctx.process_input(value.clone()); + Self { + type: value.get("type").and_then(|v| v.as_str()).unwrap_or_default().to_string(), + id: value.get("id").and_then(|v| v.as_str()).unwrap_or_default().to_string(), + name: value.get("name").and_then(|v| v.as_str()).unwrap_or_default().to_string(), + input: value.get("input").cloned().unwrap_or(serde_json::Value::Null), + } + } + + /// Serialize AnthropicToolUseBlock to a `serde_json::Value`. + /// + /// Calls `ctx.process_dict` after serialization. + pub fn to_value(&self, ctx: &SaveContext) -> serde_json::Value { + let mut result = serde_json::Map::new(); + // Write base fields + if !self.type.is_empty() { + result.insert("type".to_string(), serde_json::Value::String(self.type.clone())); + } + if !self.id.is_empty() { + result.insert("id".to_string(), serde_json::Value::String(self.id.clone())); + } + if !self.name.is_empty() { + result.insert("name".to_string(), serde_json::Value::String(self.name.clone())); + } + if !self.input.is_null() { + result.insert("input".to_string(), self.input.clone()); + } + ctx.process_dict(serde_json::Value::Object(result)) + } + + /// Serialize AnthropicToolUseBlock to a JSON string. + pub fn to_json(&self, ctx: &SaveContext) -> Result { + serde_json::to_string_pretty(&self.to_value(ctx)) + } + + /// Serialize AnthropicToolUseBlock to a YAML string. + pub fn to_yaml(&self, ctx: &SaveContext) -> Result { + serde_yaml::to_string(&self.to_value(ctx)) + } + /// Returns typed reference to the map if the field is an object. + /// Returns `None` if the field is null or not an object. + pub fn as_input_dict(&self) -> Option<&serde_json::Map> { + self.input.as_object() + } + +} diff --git a/runtime/rust/prompty/src/model/wire/anthropic_usage.rs b/runtime/rust/prompty/src/model/wire/anthropic_usage.rs new file mode 100644 index 00000000..be84434a --- /dev/null +++ b/runtime/rust/prompty/src/model/wire/anthropic_usage.rs @@ -0,0 +1,69 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +use super::super::context::{LoadContext, SaveContext}; + +/// Usage statistics returned in an Anthropic Messages API response. +#[derive(Debug, Clone, Default)] +pub struct AnthropicUsage { + /// Number of input tokens consumed + pub input_tokens: i32, + /// Number of output tokens generated + pub output_tokens: i32, +} + +impl AnthropicUsage { + /// Create a new AnthropicUsage with default values. + pub fn new() -> Self { + Self::default() + } + + /// Load AnthropicUsage from a JSON string. + pub fn from_json(json: &str, ctx: &LoadContext) -> Result { + let value: serde_json::Value = serde_json::from_str(json)?; + Ok(Self::load_from_value(&value, ctx)) + } + + /// Load AnthropicUsage from a YAML string. + pub fn from_yaml(yaml: &str, ctx: &LoadContext) -> Result { + let value: serde_json::Value = serde_yaml::from_str(yaml)?; + Ok(Self::load_from_value(&value, ctx)) + } + + /// Load AnthropicUsage from a `serde_json::Value`. + /// + /// Calls `ctx.process_input` before field extraction. + pub fn load_from_value(value: &serde_json::Value, ctx: &LoadContext) -> Self { + let value = ctx.process_input(value.clone()); + Self { + input_tokens: value.get("input_tokens").and_then(|v| v.as_i64()).unwrap_or(0) as i32, + output_tokens: value.get("output_tokens").and_then(|v| v.as_i64()).unwrap_or(0) as i32, + } + } + + /// Serialize AnthropicUsage to a `serde_json::Value`. + /// + /// Calls `ctx.process_dict` after serialization. + pub fn to_value(&self, ctx: &SaveContext) -> serde_json::Value { + let mut result = serde_json::Map::new(); + // Write base fields + if self.input_tokens != 0 { + result.insert("input_tokens".to_string(), serde_json::Value::Number(serde_json::Number::from(self.input_tokens))); + } + if self.output_tokens != 0 { + result.insert("output_tokens".to_string(), serde_json::Value::Number(serde_json::Number::from(self.output_tokens))); + } + ctx.process_dict(serde_json::Value::Object(result)) + } + + /// Serialize AnthropicUsage to a JSON string. + pub fn to_json(&self, ctx: &SaveContext) -> Result { + serde_json::to_string_pretty(&self.to_value(ctx)) + } + + /// Serialize AnthropicUsage to a YAML string. + pub fn to_yaml(&self, ctx: &SaveContext) -> Result { + serde_yaml::to_string(&self.to_value(ctx)) + } +} diff --git a/runtime/rust/prompty/src/model/wire/anthropic_wire_message.rs b/runtime/rust/prompty/src/model/wire/anthropic_wire_message.rs new file mode 100644 index 00000000..b00faff2 --- /dev/null +++ b/runtime/rust/prompty/src/model/wire/anthropic_wire_message.rs @@ -0,0 +1,69 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +use super::super::context::{LoadContext, SaveContext}; + +/// A single message in the Anthropic Messages API wire format. Anthropic always uses the array-of-blocks form for content, even when there is only one text block (§7.5). +#[derive(Debug, Clone, Default)] +pub struct AnthropicWireMessage { + /// The message role ('user' or 'assistant') + pub role: String, + /// Array of typed content blocks (AnthropicTextBlock | AnthropicImageBlock | AnthropicToolUseBlock | AnthropicToolResultBlock) + pub content: Vec, +} + +impl AnthropicWireMessage { + /// Create a new AnthropicWireMessage with default values. + pub fn new() -> Self { + Self::default() + } + + /// Load AnthropicWireMessage from a JSON string. + pub fn from_json(json: &str, ctx: &LoadContext) -> Result { + let value: serde_json::Value = serde_json::from_str(json)?; + Ok(Self::load_from_value(&value, ctx)) + } + + /// Load AnthropicWireMessage from a YAML string. + pub fn from_yaml(yaml: &str, ctx: &LoadContext) -> Result { + let value: serde_json::Value = serde_yaml::from_str(yaml)?; + Ok(Self::load_from_value(&value, ctx)) + } + + /// Load AnthropicWireMessage from a `serde_json::Value`. + /// + /// Calls `ctx.process_input` before field extraction. + pub fn load_from_value(value: &serde_json::Value, ctx: &LoadContext) -> Self { + let value = ctx.process_input(value.clone()); + Self { + role: value.get("role").and_then(|v| v.as_str()).unwrap_or_default().to_string(), + content: value.get("content").and_then(|v| v.as_array()).map(|arr| arr.to_vec()).unwrap_or_default(), + } + } + + /// Serialize AnthropicWireMessage to a `serde_json::Value`. + /// + /// Calls `ctx.process_dict` after serialization. + pub fn to_value(&self, ctx: &SaveContext) -> serde_json::Value { + let mut result = serde_json::Map::new(); + // Write base fields + if !self.role.is_empty() { + result.insert("role".to_string(), serde_json::Value::String(self.role.clone())); + } + if !self.content.is_empty() { + result.insert("content".to_string(), serde_json::to_value(&self.content).unwrap_or(serde_json::Value::Null)); + } + ctx.process_dict(serde_json::Value::Object(result)) + } + + /// Serialize AnthropicWireMessage to a JSON string. + pub fn to_json(&self, ctx: &SaveContext) -> Result { + serde_json::to_string_pretty(&self.to_value(ctx)) + } + + /// Serialize AnthropicWireMessage to a YAML string. + pub fn to_yaml(&self, ctx: &SaveContext) -> Result { + serde_yaml::to_string(&self.to_value(ctx)) + } +} diff --git a/runtime/rust/prompty/src/model/wire/mod.rs b/runtime/rust/prompty/src/model/wire/mod.rs new file mode 100644 index 00000000..64fb5e4c --- /dev/null +++ b/runtime/rust/prompty/src/model/wire/mod.rs @@ -0,0 +1,33 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +pub mod anthropic_text_block; +pub use anthropic_text_block::*; + +pub mod anthropic_image_source; +pub use anthropic_image_source::*; + +pub mod anthropic_image_block; +pub use anthropic_image_block::*; + +pub mod anthropic_tool_use_block; +pub use anthropic_tool_use_block::*; + +pub mod anthropic_tool_result_block; +pub use anthropic_tool_result_block::*; + +pub mod anthropic_wire_message; +pub use anthropic_wire_message::*; + +pub mod anthropic_tool_definition; +pub use anthropic_tool_definition::*; + +pub mod anthropic_messages_request; +pub use anthropic_messages_request::*; + +pub mod anthropic_usage; +pub use anthropic_usage::*; + +pub mod anthropic_messages_response; +pub use anthropic_messages_response::*; diff --git a/runtime/rust/prompty/tests/model/core/file_not_found_error_test.rs b/runtime/rust/prompty/tests/model/core/file_not_found_error_test.rs new file mode 100644 index 00000000..69588e8f --- /dev/null +++ b/runtime/rust/prompty/tests/model/core/file_not_found_error_test.rs @@ -0,0 +1,55 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +use prompty::model::FileNotFoundError; +use prompty::model::context::{LoadContext, SaveContext}; + +#[test] +fn test_file_not_found_error_load_json() { + let json = r####" +{ + "message": "Prompty file not found: ./chat.prompty", + "path": "./chat.prompty" +} +"####; + let ctx = LoadContext::default(); + let result = FileNotFoundError::from_json(json, &ctx); + assert!(result.is_ok(), "Failed to load from JSON: {:?}", result.err()); + let instance = result.unwrap(); + assert_eq!(instance.message, "Prompty file not found: ./chat.prompty"); + assert_eq!(instance.path, "./chat.prompty"); +} + +#[test] +fn test_file_not_found_error_load_yaml() { + let yaml = r####" +message: "Prompty file not found: ./chat.prompty" +path: ./chat.prompty + +"####; + let ctx = LoadContext::default(); + let result = FileNotFoundError::from_yaml(yaml, &ctx); + assert!(result.is_ok(), "Failed to load from YAML: {:?}", result.err()); + let instance = result.unwrap(); + assert_eq!(instance.message, "Prompty file not found: ./chat.prompty"); + assert_eq!(instance.path, "./chat.prompty"); +} + +#[test] +fn test_file_not_found_error_roundtrip() { + let json = r####" +{ + "message": "Prompty file not found: ./chat.prompty", + "path": "./chat.prompty" +} +"####; + let load_ctx = LoadContext::default(); + let result = FileNotFoundError::from_json(json, &load_ctx); + assert!(result.is_ok(), "Failed to load: {:?}", result.err()); + let instance = result.unwrap(); + let save_ctx = SaveContext::default(); + let json_output = instance.to_json(&save_ctx); + assert!(json_output.is_ok(), "Failed to serialize to JSON: {:?}", json_output.err()); +} + diff --git a/runtime/rust/prompty/tests/model/core/invoker_error_test.rs b/runtime/rust/prompty/tests/model/core/invoker_error_test.rs new file mode 100644 index 00000000..e75bf386 --- /dev/null +++ b/runtime/rust/prompty/tests/model/core/invoker_error_test.rs @@ -0,0 +1,60 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +use prompty::model::InvokerError; +use prompty::model::context::{LoadContext, SaveContext}; + +#[test] +fn test_invoker_error_load_json() { + let json = r####" +{ + "message": "No renderer registered for key: jinja2", + "component": "renderer", + "key": "jinja2" +} +"####; + let ctx = LoadContext::default(); + let result = InvokerError::from_json(json, &ctx); + assert!(result.is_ok(), "Failed to load from JSON: {:?}", result.err()); + let instance = result.unwrap(); + assert_eq!(instance.message, "No renderer registered for key: jinja2"); + assert_eq!(instance.component, "renderer"); + assert_eq!(instance.key, "jinja2"); +} + +#[test] +fn test_invoker_error_load_yaml() { + let yaml = r####" +message: "No renderer registered for key: jinja2" +component: renderer +key: jinja2 + +"####; + let ctx = LoadContext::default(); + let result = InvokerError::from_yaml(yaml, &ctx); + assert!(result.is_ok(), "Failed to load from YAML: {:?}", result.err()); + let instance = result.unwrap(); + assert_eq!(instance.message, "No renderer registered for key: jinja2"); + assert_eq!(instance.component, "renderer"); + assert_eq!(instance.key, "jinja2"); +} + +#[test] +fn test_invoker_error_roundtrip() { + let json = r####" +{ + "message": "No renderer registered for key: jinja2", + "component": "renderer", + "key": "jinja2" +} +"####; + let load_ctx = LoadContext::default(); + let result = InvokerError::from_json(json, &load_ctx); + assert!(result.is_ok(), "Failed to load: {:?}", result.err()); + let instance = result.unwrap(); + let save_ctx = SaveContext::default(); + let json_output = instance.to_json(&save_ctx); + assert!(json_output.is_ok(), "Failed to serialize to JSON: {:?}", json_output.err()); +} + diff --git a/runtime/rust/prompty/tests/model/core/validation_error_test.rs b/runtime/rust/prompty/tests/model/core/validation_error_test.rs new file mode 100644 index 00000000..1780a81e --- /dev/null +++ b/runtime/rust/prompty/tests/model/core/validation_error_test.rs @@ -0,0 +1,60 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +use prompty::model::ValidationError; +use prompty::model::context::{LoadContext, SaveContext}; + +#[test] +fn test_validation_error_load_json() { + let json = r####" +{ + "message": "Missing required input: firstName", + "property": "firstName", + "constraint": "required" +} +"####; + let ctx = LoadContext::default(); + let result = ValidationError::from_json(json, &ctx); + assert!(result.is_ok(), "Failed to load from JSON: {:?}", result.err()); + let instance = result.unwrap(); + assert_eq!(instance.message, "Missing required input: firstName"); + assert_eq!(instance.property, "firstName"); + assert_eq!(instance.constraint, "required"); +} + +#[test] +fn test_validation_error_load_yaml() { + let yaml = r####" +message: "Missing required input: firstName" +property: firstName +constraint: required + +"####; + let ctx = LoadContext::default(); + let result = ValidationError::from_yaml(yaml, &ctx); + assert!(result.is_ok(), "Failed to load from YAML: {:?}", result.err()); + let instance = result.unwrap(); + assert_eq!(instance.message, "Missing required input: firstName"); + assert_eq!(instance.property, "firstName"); + assert_eq!(instance.constraint, "required"); +} + +#[test] +fn test_validation_error_roundtrip() { + let json = r####" +{ + "message": "Missing required input: firstName", + "property": "firstName", + "constraint": "required" +} +"####; + let load_ctx = LoadContext::default(); + let result = ValidationError::from_json(json, &load_ctx); + assert!(result.is_ok(), "Failed to load: {:?}", result.err()); + let instance = result.unwrap(); + let save_ctx = SaveContext::default(); + let json_output = instance.to_json(&save_ctx); + assert!(json_output.is_ok(), "Failed to serialize to JSON: {:?}", json_output.err()); +} + diff --git a/runtime/rust/prompty/tests/model/core/validation_result_test.rs b/runtime/rust/prompty/tests/model/core/validation_result_test.rs new file mode 100644 index 00000000..26beedbb --- /dev/null +++ b/runtime/rust/prompty/tests/model/core/validation_result_test.rs @@ -0,0 +1,53 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +use prompty::model::ValidationResult; +use prompty::model::context::{LoadContext, SaveContext}; + +#[test] +fn test_validation_result_load_json() { + let json = r####" +{ + "valid": true, + "errors": [] +} +"####; + let ctx = LoadContext::default(); + let result = ValidationResult::from_json(json, &ctx); + assert!(result.is_ok(), "Failed to load from JSON: {:?}", result.err()); + let instance = result.unwrap(); + assert_eq!(instance.valid, true); +} + +#[test] +fn test_validation_result_load_yaml() { + let yaml = r####" +valid: true +errors: [] + +"####; + let ctx = LoadContext::default(); + let result = ValidationResult::from_yaml(yaml, &ctx); + assert!(result.is_ok(), "Failed to load from YAML: {:?}", result.err()); + let instance = result.unwrap(); + assert_eq!(instance.valid, true); +} + +#[test] +fn test_validation_result_roundtrip() { + let json = r####" +{ + "valid": true, + "errors": [] +} +"####; + let load_ctx = LoadContext::default(); + let result = ValidationResult::from_json(json, &load_ctx); + assert!(result.is_ok(), "Failed to load: {:?}", result.err()); + let instance = result.unwrap(); + let save_ctx = SaveContext::default(); + let json_output = instance.to_json(&save_ctx); + assert!(json_output.is_ok(), "Failed to serialize to JSON: {:?}", json_output.err()); +} + diff --git a/runtime/rust/prompty/tests/model/streaming/mod.rs b/runtime/rust/prompty/tests/model/streaming/mod.rs new file mode 100644 index 00000000..3ad196f8 --- /dev/null +++ b/runtime/rust/prompty/tests/model/streaming/mod.rs @@ -0,0 +1,5 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +mod stream_options_test; diff --git a/runtime/rust/prompty/tests/model/streaming/stream_options_test.rs b/runtime/rust/prompty/tests/model/streaming/stream_options_test.rs new file mode 100644 index 00000000..78b2c50c --- /dev/null +++ b/runtime/rust/prompty/tests/model/streaming/stream_options_test.rs @@ -0,0 +1,51 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +use prompty::model::StreamOptions; +use prompty::model::context::{LoadContext, SaveContext}; + +#[test] +fn test_stream_options_load_json() { + let json = r####" +{ + "includeUsage": true +} +"####; + let ctx = LoadContext::default(); + let result = StreamOptions::from_json(json, &ctx); + assert!(result.is_ok(), "Failed to load from JSON: {:?}", result.err()); + let instance = result.unwrap(); + assert!(instance.include_usage.is_some(), "Expected include_usage to be Some"); + assert_eq!(instance.include_usage.as_ref().unwrap(), &true); +} + +#[test] +fn test_stream_options_load_yaml() { + let yaml = r####" +includeUsage: true + +"####; + let ctx = LoadContext::default(); + let result = StreamOptions::from_yaml(yaml, &ctx); + assert!(result.is_ok(), "Failed to load from YAML: {:?}", result.err()); + let instance = result.unwrap(); + assert!(instance.include_usage.is_some(), "Expected include_usage to be Some"); +} + +#[test] +fn test_stream_options_roundtrip() { + let json = r####" +{ + "includeUsage": true +} +"####; + let load_ctx = LoadContext::default(); + let result = StreamOptions::from_json(json, &load_ctx); + assert!(result.is_ok(), "Failed to load: {:?}", result.err()); + let instance = result.unwrap(); + let save_ctx = SaveContext::default(); + let json_output = instance.to_json(&save_ctx); + assert!(json_output.is_ok(), "Failed to serialize to JSON: {:?}", json_output.err()); +} + diff --git a/runtime/rust/prompty/tests/model/tracing/mod.rs b/runtime/rust/prompty/tests/model/tracing/mod.rs new file mode 100644 index 00000000..fae4c27a --- /dev/null +++ b/runtime/rust/prompty/tests/model/tracing/mod.rs @@ -0,0 +1,7 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +mod trace_time_test; +mod trace_span_test; +mod trace_file_test; diff --git a/runtime/rust/prompty/tests/model/tracing/trace_file_test.rs b/runtime/rust/prompty/tests/model/tracing/trace_file_test.rs new file mode 100644 index 00000000..8281f235 --- /dev/null +++ b/runtime/rust/prompty/tests/model/tracing/trace_file_test.rs @@ -0,0 +1,55 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +use prompty::model::TraceFile; +use prompty::model::context::{LoadContext, SaveContext}; + +#[test] +fn test_trace_file_load_json() { + let json = r####" +{ + "runtime": "python", + "version": "2.0.0" +} +"####; + let ctx = LoadContext::default(); + let result = TraceFile::from_json(json, &ctx); + assert!(result.is_ok(), "Failed to load from JSON: {:?}", result.err()); + let instance = result.unwrap(); + assert_eq!(instance.runtime, "python"); + assert_eq!(instance.version, "2.0.0"); +} + +#[test] +fn test_trace_file_load_yaml() { + let yaml = r####" +runtime: python +version: 2.0.0 + +"####; + let ctx = LoadContext::default(); + let result = TraceFile::from_yaml(yaml, &ctx); + assert!(result.is_ok(), "Failed to load from YAML: {:?}", result.err()); + let instance = result.unwrap(); + assert_eq!(instance.runtime, "python"); + assert_eq!(instance.version, "2.0.0"); +} + +#[test] +fn test_trace_file_roundtrip() { + let json = r####" +{ + "runtime": "python", + "version": "2.0.0" +} +"####; + let load_ctx = LoadContext::default(); + let result = TraceFile::from_json(json, &load_ctx); + assert!(result.is_ok(), "Failed to load: {:?}", result.err()); + let instance = result.unwrap(); + let save_ctx = SaveContext::default(); + let json_output = instance.to_json(&save_ctx); + assert!(json_output.is_ok(), "Failed to serialize to JSON: {:?}", json_output.err()); +} + diff --git a/runtime/rust/prompty/tests/model/tracing/trace_span_test.rs b/runtime/rust/prompty/tests/model/tracing/trace_span_test.rs new file mode 100644 index 00000000..3e4dd6ba --- /dev/null +++ b/runtime/rust/prompty/tests/model/tracing/trace_span_test.rs @@ -0,0 +1,62 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +use prompty::model::TraceSpan; +use prompty::model::context::{LoadContext, SaveContext}; + +#[test] +fn test_trace_span_load_json() { + let json = r####" +{ + "name": "prompty.core.pipeline.run", + "signature": "prompty.core.pipeline.run", + "error": "Connection refused" +} +"####; + let ctx = LoadContext::default(); + let result = TraceSpan::from_json(json, &ctx); + assert!(result.is_ok(), "Failed to load from JSON: {:?}", result.err()); + let instance = result.unwrap(); + assert_eq!(instance.name, "prompty.core.pipeline.run"); + assert!(instance.signature.is_some(), "Expected signature to be Some"); + assert_eq!(instance.signature.as_ref().unwrap(), &"prompty.core.pipeline.run"); + assert!(instance.error.is_some(), "Expected error to be Some"); + assert_eq!(instance.error.as_ref().unwrap(), &"Connection refused"); +} + +#[test] +fn test_trace_span_load_yaml() { + let yaml = r####" +name: prompty.core.pipeline.run +signature: prompty.core.pipeline.run +error: Connection refused + +"####; + let ctx = LoadContext::default(); + let result = TraceSpan::from_yaml(yaml, &ctx); + assert!(result.is_ok(), "Failed to load from YAML: {:?}", result.err()); + let instance = result.unwrap(); + assert_eq!(instance.name, "prompty.core.pipeline.run"); + assert!(instance.signature.is_some(), "Expected signature to be Some"); + assert!(instance.error.is_some(), "Expected error to be Some"); +} + +#[test] +fn test_trace_span_roundtrip() { + let json = r####" +{ + "name": "prompty.core.pipeline.run", + "signature": "prompty.core.pipeline.run", + "error": "Connection refused" +} +"####; + let load_ctx = LoadContext::default(); + let result = TraceSpan::from_json(json, &load_ctx); + assert!(result.is_ok(), "Failed to load: {:?}", result.err()); + let instance = result.unwrap(); + let save_ctx = SaveContext::default(); + let json_output = instance.to_json(&save_ctx); + assert!(json_output.is_ok(), "Failed to serialize to JSON: {:?}", json_output.err()); +} + diff --git a/runtime/rust/prompty/tests/model/tracing/trace_time_test.rs b/runtime/rust/prompty/tests/model/tracing/trace_time_test.rs new file mode 100644 index 00000000..a4e80428 --- /dev/null +++ b/runtime/rust/prompty/tests/model/tracing/trace_time_test.rs @@ -0,0 +1,60 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +use prompty::model::TraceTime; +use prompty::model::context::{LoadContext, SaveContext}; + +#[test] +fn test_trace_time_load_json() { + let json = r####" +{ + "start": "2026-04-04T12:00:00Z", + "end": "2026-04-04T12:00:01Z", + "duration": 1000 +} +"####; + let ctx = LoadContext::default(); + let result = TraceTime::from_json(json, &ctx); + assert!(result.is_ok(), "Failed to load from JSON: {:?}", result.err()); + let instance = result.unwrap(); + assert_eq!(instance.start, "2026-04-04T12:00:00Z"); + assert_eq!(instance.end, "2026-04-04T12:00:01Z"); + assert_eq!(instance.duration, 1000); +} + +#[test] +fn test_trace_time_load_yaml() { + let yaml = r####" +start: "2026-04-04T12:00:00Z" +end: "2026-04-04T12:00:01Z" +duration: 1000 + +"####; + let ctx = LoadContext::default(); + let result = TraceTime::from_yaml(yaml, &ctx); + assert!(result.is_ok(), "Failed to load from YAML: {:?}", result.err()); + let instance = result.unwrap(); + assert_eq!(instance.start, "2026-04-04T12:00:00Z"); + assert_eq!(instance.end, "2026-04-04T12:00:01Z"); + assert_eq!(instance.duration, 1000); +} + +#[test] +fn test_trace_time_roundtrip() { + let json = r####" +{ + "start": "2026-04-04T12:00:00Z", + "end": "2026-04-04T12:00:01Z", + "duration": 1000 +} +"####; + let load_ctx = LoadContext::default(); + let result = TraceTime::from_json(json, &load_ctx); + assert!(result.is_ok(), "Failed to load: {:?}", result.err()); + let instance = result.unwrap(); + let save_ctx = SaveContext::default(); + let json_output = instance.to_json(&save_ctx); + assert!(json_output.is_ok(), "Failed to serialize to JSON: {:?}", json_output.err()); +} + diff --git a/runtime/rust/prompty/tests/model/wire/anthropic_image_block_test.rs b/runtime/rust/prompty/tests/model/wire/anthropic_image_block_test.rs new file mode 100644 index 00000000..be1519c4 --- /dev/null +++ b/runtime/rust/prompty/tests/model/wire/anthropic_image_block_test.rs @@ -0,0 +1,7 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +use prompty::model::AnthropicImageBlock; +use prompty::model::context::{LoadContext, SaveContext}; + diff --git a/runtime/rust/prompty/tests/model/wire/anthropic_image_source_test.rs b/runtime/rust/prompty/tests/model/wire/anthropic_image_source_test.rs new file mode 100644 index 00000000..a7e11368 --- /dev/null +++ b/runtime/rust/prompty/tests/model/wire/anthropic_image_source_test.rs @@ -0,0 +1,55 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +use prompty::model::AnthropicImageSource; +use prompty::model::context::{LoadContext, SaveContext}; + +#[test] +fn test_anthropic_image_source_load_json() { + let json = r####" +{ + "media_type": "image/png", + "data": "iVBORw0KGgo..." +} +"####; + let ctx = LoadContext::default(); + let result = AnthropicImageSource::from_json(json, &ctx); + assert!(result.is_ok(), "Failed to load from JSON: {:?}", result.err()); + let instance = result.unwrap(); + assert_eq!(instance.media_type, "image/png"); + assert_eq!(instance.data, "iVBORw0KGgo..."); +} + +#[test] +fn test_anthropic_image_source_load_yaml() { + let yaml = r####" +media_type: image/png +data: iVBORw0KGgo... + +"####; + let ctx = LoadContext::default(); + let result = AnthropicImageSource::from_yaml(yaml, &ctx); + assert!(result.is_ok(), "Failed to load from YAML: {:?}", result.err()); + let instance = result.unwrap(); + assert_eq!(instance.media_type, "image/png"); + assert_eq!(instance.data, "iVBORw0KGgo..."); +} + +#[test] +fn test_anthropic_image_source_roundtrip() { + let json = r####" +{ + "media_type": "image/png", + "data": "iVBORw0KGgo..." +} +"####; + let load_ctx = LoadContext::default(); + let result = AnthropicImageSource::from_json(json, &load_ctx); + assert!(result.is_ok(), "Failed to load: {:?}", result.err()); + let instance = result.unwrap(); + let save_ctx = SaveContext::default(); + let json_output = instance.to_json(&save_ctx); + assert!(json_output.is_ok(), "Failed to serialize to JSON: {:?}", json_output.err()); +} + diff --git a/runtime/rust/prompty/tests/model/wire/anthropic_messages_request_test.rs b/runtime/rust/prompty/tests/model/wire/anthropic_messages_request_test.rs new file mode 100644 index 00000000..625555e1 --- /dev/null +++ b/runtime/rust/prompty/tests/model/wire/anthropic_messages_request_test.rs @@ -0,0 +1,87 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +use prompty::model::AnthropicMessagesRequest; +use prompty::model::context::{LoadContext, SaveContext}; + +#[test] +fn test_anthropic_messages_request_load_json() { + let json = r####" +{ + "model": "claude-sonnet-4-20250514", + "max_tokens": 4096, + "system": "You are a helpful assistant.", + "temperature": 0.7, + "top_p": 0.9, + "top_k": 40, + "stop_sequences": [ + "\n\nHuman:" + ] +} +"####; + let ctx = LoadContext::default(); + let result = AnthropicMessagesRequest::from_json(json, &ctx); + assert!(result.is_ok(), "Failed to load from JSON: {:?}", result.err()); + let instance = result.unwrap(); + assert_eq!(instance.model, "claude-sonnet-4-20250514"); + assert_eq!(instance.max_tokens, 4096); + assert!(instance.system.is_some(), "Expected system to be Some"); + assert_eq!(instance.system.as_ref().unwrap(), &"You are a helpful assistant."); + assert!(instance.temperature.is_some(), "Expected temperature to be Some"); + assert_eq!(instance.temperature.as_ref().unwrap(), &0.7); + assert!(instance.top_p.is_some(), "Expected top_p to be Some"); + assert_eq!(instance.top_p.as_ref().unwrap(), &0.9); + assert!(instance.top_k.is_some(), "Expected top_k to be Some"); + assert_eq!(instance.top_k.as_ref().unwrap(), &40); +} + +#[test] +fn test_anthropic_messages_request_load_yaml() { + let yaml = r####" +model: claude-sonnet-4-20250514 +max_tokens: 4096 +system: You are a helpful assistant. +temperature: 0.7 +top_p: 0.9 +top_k: 40 +stop_sequences: + - "\n\nHuman:" + +"####; + let ctx = LoadContext::default(); + let result = AnthropicMessagesRequest::from_yaml(yaml, &ctx); + assert!(result.is_ok(), "Failed to load from YAML: {:?}", result.err()); + let instance = result.unwrap(); + assert_eq!(instance.model, "claude-sonnet-4-20250514"); + assert_eq!(instance.max_tokens, 4096); + assert!(instance.system.is_some(), "Expected system to be Some"); + assert!(instance.temperature.is_some(), "Expected temperature to be Some"); + assert!(instance.top_p.is_some(), "Expected top_p to be Some"); + assert!(instance.top_k.is_some(), "Expected top_k to be Some"); +} + +#[test] +fn test_anthropic_messages_request_roundtrip() { + let json = r####" +{ + "model": "claude-sonnet-4-20250514", + "max_tokens": 4096, + "system": "You are a helpful assistant.", + "temperature": 0.7, + "top_p": 0.9, + "top_k": 40, + "stop_sequences": [ + "\n\nHuman:" + ] +} +"####; + let load_ctx = LoadContext::default(); + let result = AnthropicMessagesRequest::from_json(json, &load_ctx); + assert!(result.is_ok(), "Failed to load: {:?}", result.err()); + let instance = result.unwrap(); + let save_ctx = SaveContext::default(); + let json_output = instance.to_json(&save_ctx); + assert!(json_output.is_ok(), "Failed to serialize to JSON: {:?}", json_output.err()); +} + diff --git a/runtime/rust/prompty/tests/model/wire/anthropic_messages_response_test.rs b/runtime/rust/prompty/tests/model/wire/anthropic_messages_response_test.rs new file mode 100644 index 00000000..d470109f --- /dev/null +++ b/runtime/rust/prompty/tests/model/wire/anthropic_messages_response_test.rs @@ -0,0 +1,60 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +use prompty::model::AnthropicMessagesResponse; +use prompty::model::context::{LoadContext, SaveContext}; + +#[test] +fn test_anthropic_messages_response_load_json() { + let json = r####" +{ + "id": "msg_01XFDUDYJgAACzvnptvVoYEL", + "model": "claude-sonnet-4-20250514", + "stop_reason": "end_turn" +} +"####; + let ctx = LoadContext::default(); + let result = AnthropicMessagesResponse::from_json(json, &ctx); + assert!(result.is_ok(), "Failed to load from JSON: {:?}", result.err()); + let instance = result.unwrap(); + assert_eq!(instance.id, "msg_01XFDUDYJgAACzvnptvVoYEL"); + assert_eq!(instance.model, "claude-sonnet-4-20250514"); + assert_eq!(instance.stop_reason, "end_turn"); +} + +#[test] +fn test_anthropic_messages_response_load_yaml() { + let yaml = r####" +id: msg_01XFDUDYJgAACzvnptvVoYEL +model: claude-sonnet-4-20250514 +stop_reason: end_turn + +"####; + let ctx = LoadContext::default(); + let result = AnthropicMessagesResponse::from_yaml(yaml, &ctx); + assert!(result.is_ok(), "Failed to load from YAML: {:?}", result.err()); + let instance = result.unwrap(); + assert_eq!(instance.id, "msg_01XFDUDYJgAACzvnptvVoYEL"); + assert_eq!(instance.model, "claude-sonnet-4-20250514"); + assert_eq!(instance.stop_reason, "end_turn"); +} + +#[test] +fn test_anthropic_messages_response_roundtrip() { + let json = r####" +{ + "id": "msg_01XFDUDYJgAACzvnptvVoYEL", + "model": "claude-sonnet-4-20250514", + "stop_reason": "end_turn" +} +"####; + let load_ctx = LoadContext::default(); + let result = AnthropicMessagesResponse::from_json(json, &load_ctx); + assert!(result.is_ok(), "Failed to load: {:?}", result.err()); + let instance = result.unwrap(); + let save_ctx = SaveContext::default(); + let json_output = instance.to_json(&save_ctx); + assert!(json_output.is_ok(), "Failed to serialize to JSON: {:?}", json_output.err()); +} + diff --git a/runtime/rust/prompty/tests/model/wire/anthropic_text_block_test.rs b/runtime/rust/prompty/tests/model/wire/anthropic_text_block_test.rs new file mode 100644 index 00000000..0d0dec4e --- /dev/null +++ b/runtime/rust/prompty/tests/model/wire/anthropic_text_block_test.rs @@ -0,0 +1,50 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +use prompty::model::AnthropicTextBlock; +use prompty::model::context::{LoadContext, SaveContext}; + +#[test] +fn test_anthropic_text_block_load_json() { + let json = r####" +{ + "text": "Hello, how can I help?" +} +"####; + let ctx = LoadContext::default(); + let result = AnthropicTextBlock::from_json(json, &ctx); + assert!(result.is_ok(), "Failed to load from JSON: {:?}", result.err()); + let instance = result.unwrap(); + assert_eq!(instance.text, "Hello, how can I help?"); +} + +#[test] +fn test_anthropic_text_block_load_yaml() { + let yaml = r####" +text: Hello, how can I help? + +"####; + let ctx = LoadContext::default(); + let result = AnthropicTextBlock::from_yaml(yaml, &ctx); + assert!(result.is_ok(), "Failed to load from YAML: {:?}", result.err()); + let instance = result.unwrap(); + assert_eq!(instance.text, "Hello, how can I help?"); +} + +#[test] +fn test_anthropic_text_block_roundtrip() { + let json = r####" +{ + "text": "Hello, how can I help?" +} +"####; + let load_ctx = LoadContext::default(); + let result = AnthropicTextBlock::from_json(json, &load_ctx); + assert!(result.is_ok(), "Failed to load: {:?}", result.err()); + let instance = result.unwrap(); + let save_ctx = SaveContext::default(); + let json_output = instance.to_json(&save_ctx); + assert!(json_output.is_ok(), "Failed to serialize to JSON: {:?}", json_output.err()); +} + diff --git a/runtime/rust/prompty/tests/model/wire/anthropic_tool_definition_test.rs b/runtime/rust/prompty/tests/model/wire/anthropic_tool_definition_test.rs new file mode 100644 index 00000000..a3f105cd --- /dev/null +++ b/runtime/rust/prompty/tests/model/wire/anthropic_tool_definition_test.rs @@ -0,0 +1,56 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +use prompty::model::AnthropicToolDefinition; +use prompty::model::context::{LoadContext, SaveContext}; + +#[test] +fn test_anthropic_tool_definition_load_json() { + let json = r####" +{ + "name": "get_weather", + "description": "Get the current weather for a city" +} +"####; + let ctx = LoadContext::default(); + let result = AnthropicToolDefinition::from_json(json, &ctx); + assert!(result.is_ok(), "Failed to load from JSON: {:?}", result.err()); + let instance = result.unwrap(); + assert_eq!(instance.name, "get_weather"); + assert!(instance.description.is_some(), "Expected description to be Some"); + assert_eq!(instance.description.as_ref().unwrap(), &"Get the current weather for a city"); +} + +#[test] +fn test_anthropic_tool_definition_load_yaml() { + let yaml = r####" +name: get_weather +description: Get the current weather for a city + +"####; + let ctx = LoadContext::default(); + let result = AnthropicToolDefinition::from_yaml(yaml, &ctx); + assert!(result.is_ok(), "Failed to load from YAML: {:?}", result.err()); + let instance = result.unwrap(); + assert_eq!(instance.name, "get_weather"); + assert!(instance.description.is_some(), "Expected description to be Some"); +} + +#[test] +fn test_anthropic_tool_definition_roundtrip() { + let json = r####" +{ + "name": "get_weather", + "description": "Get the current weather for a city" +} +"####; + let load_ctx = LoadContext::default(); + let result = AnthropicToolDefinition::from_json(json, &load_ctx); + assert!(result.is_ok(), "Failed to load: {:?}", result.err()); + let instance = result.unwrap(); + let save_ctx = SaveContext::default(); + let json_output = instance.to_json(&save_ctx); + assert!(json_output.is_ok(), "Failed to serialize to JSON: {:?}", json_output.err()); +} + diff --git a/runtime/rust/prompty/tests/model/wire/anthropic_tool_result_block_test.rs b/runtime/rust/prompty/tests/model/wire/anthropic_tool_result_block_test.rs new file mode 100644 index 00000000..99200873 --- /dev/null +++ b/runtime/rust/prompty/tests/model/wire/anthropic_tool_result_block_test.rs @@ -0,0 +1,55 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +use prompty::model::AnthropicToolResultBlock; +use prompty::model::context::{LoadContext, SaveContext}; + +#[test] +fn test_anthropic_tool_result_block_load_json() { + let json = r####" +{ + "tool_use_id": "toolu_01A09q90qw90lq917835lq9", + "content": "72°F and sunny in Paris" +} +"####; + let ctx = LoadContext::default(); + let result = AnthropicToolResultBlock::from_json(json, &ctx); + assert!(result.is_ok(), "Failed to load from JSON: {:?}", result.err()); + let instance = result.unwrap(); + assert_eq!(instance.tool_use_id, "toolu_01A09q90qw90lq917835lq9"); + assert_eq!(instance.content, "72°F and sunny in Paris"); +} + +#[test] +fn test_anthropic_tool_result_block_load_yaml() { + let yaml = r####" +tool_use_id: toolu_01A09q90qw90lq917835lq9 +content: 72°F and sunny in Paris + +"####; + let ctx = LoadContext::default(); + let result = AnthropicToolResultBlock::from_yaml(yaml, &ctx); + assert!(result.is_ok(), "Failed to load from YAML: {:?}", result.err()); + let instance = result.unwrap(); + assert_eq!(instance.tool_use_id, "toolu_01A09q90qw90lq917835lq9"); + assert_eq!(instance.content, "72°F and sunny in Paris"); +} + +#[test] +fn test_anthropic_tool_result_block_roundtrip() { + let json = r####" +{ + "tool_use_id": "toolu_01A09q90qw90lq917835lq9", + "content": "72°F and sunny in Paris" +} +"####; + let load_ctx = LoadContext::default(); + let result = AnthropicToolResultBlock::from_json(json, &load_ctx); + assert!(result.is_ok(), "Failed to load: {:?}", result.err()); + let instance = result.unwrap(); + let save_ctx = SaveContext::default(); + let json_output = instance.to_json(&save_ctx); + assert!(json_output.is_ok(), "Failed to serialize to JSON: {:?}", json_output.err()); +} + diff --git a/runtime/rust/prompty/tests/model/wire/anthropic_tool_use_block_test.rs b/runtime/rust/prompty/tests/model/wire/anthropic_tool_use_block_test.rs new file mode 100644 index 00000000..fbfe2f00 --- /dev/null +++ b/runtime/rust/prompty/tests/model/wire/anthropic_tool_use_block_test.rs @@ -0,0 +1,63 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +use prompty::model::AnthropicToolUseBlock; +use prompty::model::context::{LoadContext, SaveContext}; + +#[test] +fn test_anthropic_tool_use_block_load_json() { + let json = r####" +{ + "id": "toolu_01A09q90qw90lq917835lq9", + "name": "get_weather", + "input": { + "city": "Paris" + } +} +"####; + let ctx = LoadContext::default(); + let result = AnthropicToolUseBlock::from_json(json, &ctx); + assert!(result.is_ok(), "Failed to load from JSON: {:?}", result.err()); + let instance = result.unwrap(); + assert_eq!(instance.id, "toolu_01A09q90qw90lq917835lq9"); + assert_eq!(instance.name, "get_weather"); +} + +#[test] +fn test_anthropic_tool_use_block_load_yaml() { + let yaml = r####" +id: toolu_01A09q90qw90lq917835lq9 +name: get_weather +input: + city: Paris + +"####; + let ctx = LoadContext::default(); + let result = AnthropicToolUseBlock::from_yaml(yaml, &ctx); + assert!(result.is_ok(), "Failed to load from YAML: {:?}", result.err()); + let instance = result.unwrap(); + assert_eq!(instance.id, "toolu_01A09q90qw90lq917835lq9"); + assert_eq!(instance.name, "get_weather"); +} + +#[test] +fn test_anthropic_tool_use_block_roundtrip() { + let json = r####" +{ + "id": "toolu_01A09q90qw90lq917835lq9", + "name": "get_weather", + "input": { + "city": "Paris" + } +} +"####; + let load_ctx = LoadContext::default(); + let result = AnthropicToolUseBlock::from_json(json, &load_ctx); + assert!(result.is_ok(), "Failed to load: {:?}", result.err()); + let instance = result.unwrap(); + let save_ctx = SaveContext::default(); + let json_output = instance.to_json(&save_ctx); + assert!(json_output.is_ok(), "Failed to serialize to JSON: {:?}", json_output.err()); +} + diff --git a/runtime/rust/prompty/tests/model/wire/anthropic_usage_test.rs b/runtime/rust/prompty/tests/model/wire/anthropic_usage_test.rs new file mode 100644 index 00000000..d30676fc --- /dev/null +++ b/runtime/rust/prompty/tests/model/wire/anthropic_usage_test.rs @@ -0,0 +1,55 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +use prompty::model::AnthropicUsage; +use prompty::model::context::{LoadContext, SaveContext}; + +#[test] +fn test_anthropic_usage_load_json() { + let json = r####" +{ + "input_tokens": 150, + "output_tokens": 42 +} +"####; + let ctx = LoadContext::default(); + let result = AnthropicUsage::from_json(json, &ctx); + assert!(result.is_ok(), "Failed to load from JSON: {:?}", result.err()); + let instance = result.unwrap(); + assert_eq!(instance.input_tokens, 150); + assert_eq!(instance.output_tokens, 42); +} + +#[test] +fn test_anthropic_usage_load_yaml() { + let yaml = r####" +input_tokens: 150 +output_tokens: 42 + +"####; + let ctx = LoadContext::default(); + let result = AnthropicUsage::from_yaml(yaml, &ctx); + assert!(result.is_ok(), "Failed to load from YAML: {:?}", result.err()); + let instance = result.unwrap(); + assert_eq!(instance.input_tokens, 150); + assert_eq!(instance.output_tokens, 42); +} + +#[test] +fn test_anthropic_usage_roundtrip() { + let json = r####" +{ + "input_tokens": 150, + "output_tokens": 42 +} +"####; + let load_ctx = LoadContext::default(); + let result = AnthropicUsage::from_json(json, &load_ctx); + assert!(result.is_ok(), "Failed to load: {:?}", result.err()); + let instance = result.unwrap(); + let save_ctx = SaveContext::default(); + let json_output = instance.to_json(&save_ctx); + assert!(json_output.is_ok(), "Failed to serialize to JSON: {:?}", json_output.err()); +} + diff --git a/runtime/rust/prompty/tests/model/wire/anthropic_wire_message_test.rs b/runtime/rust/prompty/tests/model/wire/anthropic_wire_message_test.rs new file mode 100644 index 00000000..9801d19b --- /dev/null +++ b/runtime/rust/prompty/tests/model/wire/anthropic_wire_message_test.rs @@ -0,0 +1,50 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +use prompty::model::AnthropicWireMessage; +use prompty::model::context::{LoadContext, SaveContext}; + +#[test] +fn test_anthropic_wire_message_load_json() { + let json = r####" +{ + "role": "user" +} +"####; + let ctx = LoadContext::default(); + let result = AnthropicWireMessage::from_json(json, &ctx); + assert!(result.is_ok(), "Failed to load from JSON: {:?}", result.err()); + let instance = result.unwrap(); + assert_eq!(instance.role, "user"); +} + +#[test] +fn test_anthropic_wire_message_load_yaml() { + let yaml = r####" +role: user + +"####; + let ctx = LoadContext::default(); + let result = AnthropicWireMessage::from_yaml(yaml, &ctx); + assert!(result.is_ok(), "Failed to load from YAML: {:?}", result.err()); + let instance = result.unwrap(); + assert_eq!(instance.role, "user"); +} + +#[test] +fn test_anthropic_wire_message_roundtrip() { + let json = r####" +{ + "role": "user" +} +"####; + let load_ctx = LoadContext::default(); + let result = AnthropicWireMessage::from_json(json, &load_ctx); + assert!(result.is_ok(), "Failed to load: {:?}", result.err()); + let instance = result.unwrap(); + let save_ctx = SaveContext::default(); + let json_output = instance.to_json(&save_ctx); + assert!(json_output.is_ok(), "Failed to serialize to JSON: {:?}", json_output.err()); +} + diff --git a/runtime/rust/prompty/tests/model/wire/mod.rs b/runtime/rust/prompty/tests/model/wire/mod.rs new file mode 100644 index 00000000..254b58e9 --- /dev/null +++ b/runtime/rust/prompty/tests/model/wire/mod.rs @@ -0,0 +1,14 @@ +// Code generated by AgentSchema emitter; DO NOT EDIT. + +#![allow(unused_imports, dead_code, non_camel_case_types, unused_variables, clippy::all)] + +mod anthropic_text_block_test; +mod anthropic_image_source_test; +mod anthropic_image_block_test; +mod anthropic_tool_use_block_test; +mod anthropic_tool_result_block_test; +mod anthropic_wire_message_test; +mod anthropic_tool_definition_test; +mod anthropic_messages_request_test; +mod anthropic_usage_test; +mod anthropic_messages_response_test; diff --git a/runtime/typescript/packages/core/src/model/core/file-not-found-error.ts b/runtime/typescript/packages/core/src/model/core/file-not-found-error.ts new file mode 100644 index 00000000..e22eda85 --- /dev/null +++ b/runtime/typescript/packages/core/src/model/core/file-not-found-error.ts @@ -0,0 +1,87 @@ +// Copyright (c) Microsoft. All rights reserved. +// WARNING: This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. + +import { LoadContext, SaveContext } from "../context"; + +export class FileNotFoundError { + static readonly shorthandProperty: string | undefined = undefined; + + message: string = ""; + path: string = ""; + + constructor(init?: Partial) { + this.message = init?.message ?? ""; + this.path = init?.path ?? ""; + } + + //#region Load Methods + + static load(data: Record, context?: LoadContext): FileNotFoundError { + if (context) { + data = context.processInput(data) as Record; + } + + const instance = new FileNotFoundError(); + + if (data["message"] !== undefined && data["message"] !== null) { + instance.message = String(data["message"]); + } + if (data["path"] !== undefined && data["path"] !== null) { + instance.path = String(data["path"]); + } + + if (context) { + return context.processOutput(instance) as FileNotFoundError; + } + return instance; + } + + //#endregion + + //#region Save Methods + + save(context?: SaveContext): Record { + let obj: this = this; + if (context) { + obj = context.processObject(obj) as this; + } + + const result: Record = {}; + + if (obj.message !== undefined && obj.message !== null) { + result["message"] = obj.message; + } + if (obj.path !== undefined && obj.path !== null) { + result["path"] = obj.path; + } + + if (context) { + return context.processDict(result); + } + return result; + } + + toYaml(context?: SaveContext): string { + context = context ?? new SaveContext(); + return context.toYaml(this.save(context)); + } + + toJson(context?: SaveContext, indent: number = 2): string { + context = context ?? new SaveContext(); + return context.toJson(this.save(context), indent); + } + + static fromJson(json: string, context?: LoadContext): FileNotFoundError { + const data = JSON.parse(json); + return FileNotFoundError.load(data as Record, context); + } + + static fromYaml(yaml: string, context?: LoadContext): FileNotFoundError { + const { parse } = require("yaml"); + const data = parse(yaml); + return FileNotFoundError.load(data as Record, context); + } + + //#endregion +} + diff --git a/runtime/typescript/packages/core/src/model/core/index.ts b/runtime/typescript/packages/core/src/model/core/index.ts index bae99a68..d7c23f27 100644 --- a/runtime/typescript/packages/core/src/model/core/index.ts +++ b/runtime/typescript/packages/core/src/model/core/index.ts @@ -6,3 +6,8 @@ export { ArrayProperty, ObjectProperty, } from "./property"; + +export { FileNotFoundError } from "./file-not-found-error"; +export { InvokerError } from "./invoker-error"; +export { ValidationError } from "./validation-error"; +export { ValidationResult } from "./validation-result"; diff --git a/runtime/typescript/packages/core/src/model/core/invoker-error.ts b/runtime/typescript/packages/core/src/model/core/invoker-error.ts new file mode 100644 index 00000000..e0a3391a --- /dev/null +++ b/runtime/typescript/packages/core/src/model/core/invoker-error.ts @@ -0,0 +1,95 @@ +// Copyright (c) Microsoft. All rights reserved. +// WARNING: This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. + +import { LoadContext, SaveContext } from "../context"; + +export class InvokerError { + static readonly shorthandProperty: string | undefined = undefined; + + message: string = ""; + component: string = ""; + key: string = ""; + + constructor(init?: Partial) { + this.message = init?.message ?? ""; + this.component = init?.component ?? ""; + this.key = init?.key ?? ""; + } + + //#region Load Methods + + static load(data: Record, context?: LoadContext): InvokerError { + if (context) { + data = context.processInput(data) as Record; + } + + const instance = new InvokerError(); + + if (data["message"] !== undefined && data["message"] !== null) { + instance.message = String(data["message"]); + } + if (data["component"] !== undefined && data["component"] !== null) { + instance.component = String(data["component"]); + } + if (data["key"] !== undefined && data["key"] !== null) { + instance.key = String(data["key"]); + } + + if (context) { + return context.processOutput(instance) as InvokerError; + } + return instance; + } + + //#endregion + + //#region Save Methods + + save(context?: SaveContext): Record { + let obj: this = this; + if (context) { + obj = context.processObject(obj) as this; + } + + const result: Record = {}; + + if (obj.message !== undefined && obj.message !== null) { + result["message"] = obj.message; + } + if (obj.component !== undefined && obj.component !== null) { + result["component"] = obj.component; + } + if (obj.key !== undefined && obj.key !== null) { + result["key"] = obj.key; + } + + if (context) { + return context.processDict(result); + } + return result; + } + + toYaml(context?: SaveContext): string { + context = context ?? new SaveContext(); + return context.toYaml(this.save(context)); + } + + toJson(context?: SaveContext, indent: number = 2): string { + context = context ?? new SaveContext(); + return context.toJson(this.save(context), indent); + } + + static fromJson(json: string, context?: LoadContext): InvokerError { + const data = JSON.parse(json); + return InvokerError.load(data as Record, context); + } + + static fromYaml(yaml: string, context?: LoadContext): InvokerError { + const { parse } = require("yaml"); + const data = parse(yaml); + return InvokerError.load(data as Record, context); + } + + //#endregion +} + diff --git a/runtime/typescript/packages/core/src/model/core/validation-error.ts b/runtime/typescript/packages/core/src/model/core/validation-error.ts new file mode 100644 index 00000000..31ff9092 --- /dev/null +++ b/runtime/typescript/packages/core/src/model/core/validation-error.ts @@ -0,0 +1,95 @@ +// Copyright (c) Microsoft. All rights reserved. +// WARNING: This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. + +import { LoadContext, SaveContext } from "../context"; + +export class ValidationError { + static readonly shorthandProperty: string | undefined = undefined; + + message: string = ""; + property: string = ""; + constraint: string = ""; + + constructor(init?: Partial) { + this.message = init?.message ?? ""; + this.property = init?.property ?? ""; + this.constraint = init?.constraint ?? ""; + } + + //#region Load Methods + + static load(data: Record, context?: LoadContext): ValidationError { + if (context) { + data = context.processInput(data) as Record; + } + + const instance = new ValidationError(); + + if (data["message"] !== undefined && data["message"] !== null) { + instance.message = String(data["message"]); + } + if (data["property"] !== undefined && data["property"] !== null) { + instance.property = String(data["property"]); + } + if (data["constraint"] !== undefined && data["constraint"] !== null) { + instance.constraint = String(data["constraint"]); + } + + if (context) { + return context.processOutput(instance) as ValidationError; + } + return instance; + } + + //#endregion + + //#region Save Methods + + save(context?: SaveContext): Record { + let obj: this = this; + if (context) { + obj = context.processObject(obj) as this; + } + + const result: Record = {}; + + if (obj.message !== undefined && obj.message !== null) { + result["message"] = obj.message; + } + if (obj.property !== undefined && obj.property !== null) { + result["property"] = obj.property; + } + if (obj.constraint !== undefined && obj.constraint !== null) { + result["constraint"] = obj.constraint; + } + + if (context) { + return context.processDict(result); + } + return result; + } + + toYaml(context?: SaveContext): string { + context = context ?? new SaveContext(); + return context.toYaml(this.save(context)); + } + + toJson(context?: SaveContext, indent: number = 2): string { + context = context ?? new SaveContext(); + return context.toJson(this.save(context), indent); + } + + static fromJson(json: string, context?: LoadContext): ValidationError { + const data = JSON.parse(json); + return ValidationError.load(data as Record, context); + } + + static fromYaml(yaml: string, context?: LoadContext): ValidationError { + const { parse } = require("yaml"); + const data = parse(yaml); + return ValidationError.load(data as Record, context); + } + + //#endregion +} + diff --git a/runtime/typescript/packages/core/src/model/core/validation-result.ts b/runtime/typescript/packages/core/src/model/core/validation-result.ts new file mode 100644 index 00000000..23e90020 --- /dev/null +++ b/runtime/typescript/packages/core/src/model/core/validation-result.ts @@ -0,0 +1,113 @@ +// Copyright (c) Microsoft. All rights reserved. +// WARNING: This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. + +import { LoadContext, SaveContext } from "../context"; +import { ValidationError } from "./validation-error"; + +export class ValidationResult { + static readonly shorthandProperty: string | undefined = undefined; + + valid: boolean = false; + errors: ValidationError[] = []; + + constructor(init?: Partial) { + this.valid = init?.valid ?? false; + this.errors = init?.errors ?? []; + } + + //#region Load Methods + + static load(data: Record, context?: LoadContext): ValidationResult { + if (context) { + data = context.processInput(data) as Record; + } + + const instance = new ValidationResult(); + + if (data["valid"] !== undefined && data["valid"] !== null) { + instance.valid = Boolean(data["valid"]); + } + if (data["errors"] !== undefined && data["errors"] !== null) { + instance.errors = ValidationResult.loadErrors(data["errors"] as unknown[], context); + } + + if (context) { + return context.processOutput(instance) as ValidationResult; + } + return instance; + } + + static loadErrors(data: Record[] | unknown[], context?: LoadContext): ValidationError[] { + if (!Array.isArray(data)) { + // Convert dict/object format to array format + const result: Record[] = []; + for (const [k, v] of Object.entries(data)) { + if (typeof v === "object" && v !== null && !Array.isArray(v)) { + result.push({ name: k, ...(v as Record) }); + } else { + result.push({ name: k, "message": v }); + } + } + data = result; + } + return data.map(item => ValidationError.load(item as Record, context)); + } + + static saveErrors(items: ValidationError[], context?: SaveContext): Record[] | Record { + if (!context) { + context = new SaveContext(); + } + + // This type doesn't have a 'name' property, so always use array format + return items.map(item => item.save(context)); + } + + //#endregion + + //#region Save Methods + + save(context?: SaveContext): Record { + let obj: this = this; + if (context) { + obj = context.processObject(obj) as this; + } + + const result: Record = {}; + + if (obj.valid !== undefined && obj.valid !== null) { + result["valid"] = obj.valid; + } + if (obj.errors !== undefined && obj.errors !== null) { + result["errors"] = ValidationResult.saveErrors(obj.errors, context); + } + + if (context) { + return context.processDict(result); + } + return result; + } + + toYaml(context?: SaveContext): string { + context = context ?? new SaveContext(); + return context.toYaml(this.save(context)); + } + + toJson(context?: SaveContext, indent: number = 2): string { + context = context ?? new SaveContext(); + return context.toJson(this.save(context), indent); + } + + static fromJson(json: string, context?: LoadContext): ValidationResult { + const data = JSON.parse(json); + return ValidationResult.load(data as Record, context); + } + + static fromYaml(yaml: string, context?: LoadContext): ValidationResult { + const { parse } = require("yaml"); + const data = parse(yaml); + return ValidationResult.load(data as Record, context); + } + + //#endregion +} + diff --git a/runtime/typescript/packages/core/src/model/index.ts b/runtime/typescript/packages/core/src/model/index.ts index 3401cc66..ea6d8ba8 100644 --- a/runtime/typescript/packages/core/src/model/index.ts +++ b/runtime/typescript/packages/core/src/model/index.ts @@ -15,6 +15,10 @@ export { ToolCall } from "./conversation/tool-call"; export { ThreadMarker } from "./conversation/thread-marker"; export { Property, ArrayProperty, ObjectProperty } from "./core/property"; +export { FileNotFoundError } from "./core/file-not-found-error"; +export { InvokerError } from "./core/invoker-error"; +export { ValidationError } from "./core/validation-error"; +export { ValidationResult } from "./core/validation-result"; export { TokenEventPayload } from "./events/token-event-payload"; export { ThinkingEventPayload } from "./events/thinking-event-payload"; @@ -49,3 +53,21 @@ export { Tool, FunctionTool, CustomTool, McpTool, OpenApiTool, PromptyTool } fro export { McpApprovalMode } from "./tools/mcp-approval-mode"; export { ToolContext } from "./tools/tool-context"; export { ToolDispatchResult } from "./tools/tool-dispatch-result"; + +export { AnthropicTextBlock } from "./wire/anthropic-text-block"; +export { AnthropicImageSource } from "./wire/anthropic-image-source"; +export { AnthropicImageBlock } from "./wire/anthropic-image-block"; +export { AnthropicToolUseBlock } from "./wire/anthropic-tool-use-block"; +export { AnthropicToolResultBlock } from "./wire/anthropic-tool-result-block"; +export { AnthropicWireMessage } from "./wire/anthropic-wire-message"; +export { AnthropicToolDefinition } from "./wire/anthropic-tool-definition"; +export { AnthropicMessagesRequest } from "./wire/anthropic-messages-request"; +export { AnthropicUsage } from "./wire/anthropic-usage"; +export { AnthropicMessagesResponse } from "./wire/anthropic-messages-response"; + +export { StreamOptions } from "./streaming/stream-options"; + +export { TraceTime } from "./tracing/trace-time"; +export { TraceSpan } from "./tracing/trace-span"; +export { TraceFile } from "./tracing/trace-file"; + diff --git a/runtime/typescript/packages/core/src/model/streaming/index.ts b/runtime/typescript/packages/core/src/model/streaming/index.ts new file mode 100644 index 00000000..931f8a82 --- /dev/null +++ b/runtime/typescript/packages/core/src/model/streaming/index.ts @@ -0,0 +1,4 @@ +// Copyright (c) Microsoft. All rights reserved. +// WARNING: This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. + +export { StreamOptions } from "./stream-options"; diff --git a/runtime/typescript/packages/core/src/model/streaming/stream-options.ts b/runtime/typescript/packages/core/src/model/streaming/stream-options.ts new file mode 100644 index 00000000..56cce0c4 --- /dev/null +++ b/runtime/typescript/packages/core/src/model/streaming/stream-options.ts @@ -0,0 +1,81 @@ +// Copyright (c) Microsoft. All rights reserved. +// WARNING: This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. + +import { LoadContext, SaveContext } from "../context"; + +export class StreamOptions { + static readonly shorthandProperty: string | undefined = undefined; + + includeUsage?: boolean | undefined; + + constructor(init?: Partial) { + if (init?.includeUsage !== undefined) { + this.includeUsage = init.includeUsage; + } + } + + //#region Load Methods + + static load(data: Record, context?: LoadContext): StreamOptions { + if (context) { + data = context.processInput(data) as Record; + } + + const instance = new StreamOptions(); + + if (data["includeUsage"] !== undefined && data["includeUsage"] !== null) { + instance.includeUsage = Boolean(data["includeUsage"]); + } + + if (context) { + return context.processOutput(instance) as StreamOptions; + } + return instance; + } + + //#endregion + + //#region Save Methods + + save(context?: SaveContext): Record { + let obj: this = this; + if (context) { + obj = context.processObject(obj) as this; + } + + const result: Record = {}; + + if (obj.includeUsage !== undefined && obj.includeUsage !== null) { + result["includeUsage"] = obj.includeUsage; + } + + if (context) { + return context.processDict(result); + } + return result; + } + + toYaml(context?: SaveContext): string { + context = context ?? new SaveContext(); + return context.toYaml(this.save(context)); + } + + toJson(context?: SaveContext, indent: number = 2): string { + context = context ?? new SaveContext(); + return context.toJson(this.save(context), indent); + } + + static fromJson(json: string, context?: LoadContext): StreamOptions { + const data = JSON.parse(json); + return StreamOptions.load(data as Record, context); + } + + static fromYaml(yaml: string, context?: LoadContext): StreamOptions { + const { parse } = require("yaml"); + const data = parse(yaml); + return StreamOptions.load(data as Record, context); + } + + //#endregion +} + diff --git a/runtime/typescript/packages/core/src/model/tracing/index.ts b/runtime/typescript/packages/core/src/model/tracing/index.ts new file mode 100644 index 00000000..2135ee97 --- /dev/null +++ b/runtime/typescript/packages/core/src/model/tracing/index.ts @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft. All rights reserved. +// WARNING: This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. + +export { TraceTime } from "./trace-time"; +export { TraceSpan } from "./trace-span"; +export { TraceFile } from "./trace-file"; diff --git a/runtime/typescript/packages/core/src/model/tracing/trace-file.ts b/runtime/typescript/packages/core/src/model/tracing/trace-file.ts new file mode 100644 index 00000000..6678810b --- /dev/null +++ b/runtime/typescript/packages/core/src/model/tracing/trace-file.ts @@ -0,0 +1,98 @@ +// Copyright (c) Microsoft. All rights reserved. +// WARNING: This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. + +import { LoadContext, SaveContext } from "../context"; +import { TraceSpan } from "./trace-span"; + +export class TraceFile { + static readonly shorthandProperty: string | undefined = undefined; + + runtime: string = ""; + version: string = ""; + trace!: TraceSpan; + + constructor(init?: Partial) { + this.runtime = init?.runtime ?? ""; + this.version = init?.version ?? ""; + if (init?.trace !== undefined) { + this.trace = init.trace; + } + } + + //#region Load Methods + + static load(data: Record, context?: LoadContext): TraceFile { + if (context) { + data = context.processInput(data) as Record; + } + + const instance = new TraceFile(); + + if (data["runtime"] !== undefined && data["runtime"] !== null) { + instance.runtime = String(data["runtime"]); + } + if (data["version"] !== undefined && data["version"] !== null) { + instance.version = String(data["version"]); + } + if (data["trace"] !== undefined && data["trace"] !== null) { + instance.trace = TraceSpan.load(data["trace"] as Record, context); + } + + if (context) { + return context.processOutput(instance) as TraceFile; + } + return instance; + } + + //#endregion + + //#region Save Methods + + save(context?: SaveContext): Record { + let obj: this = this; + if (context) { + obj = context.processObject(obj) as this; + } + + const result: Record = {}; + + if (obj.runtime !== undefined && obj.runtime !== null) { + result["runtime"] = obj.runtime; + } + if (obj.version !== undefined && obj.version !== null) { + result["version"] = obj.version; + } + if (obj.trace !== undefined && obj.trace !== null) { + result["trace"] = obj.trace.save(context); + } + + if (context) { + return context.processDict(result); + } + return result; + } + + toYaml(context?: SaveContext): string { + context = context ?? new SaveContext(); + return context.toYaml(this.save(context)); + } + + toJson(context?: SaveContext, indent: number = 2): string { + context = context ?? new SaveContext(); + return context.toJson(this.save(context), indent); + } + + static fromJson(json: string, context?: LoadContext): TraceFile { + const data = JSON.parse(json); + return TraceFile.load(data as Record, context); + } + + static fromYaml(yaml: string, context?: LoadContext): TraceFile { + const { parse } = require("yaml"); + const data = parse(yaml); + return TraceFile.load(data as Record, context); + } + + //#endregion +} + diff --git a/runtime/typescript/packages/core/src/model/tracing/trace-span.ts b/runtime/typescript/packages/core/src/model/tracing/trace-span.ts new file mode 100644 index 00000000..6ab52d11 --- /dev/null +++ b/runtime/typescript/packages/core/src/model/tracing/trace-span.ts @@ -0,0 +1,161 @@ +// Copyright (c) Microsoft. All rights reserved. +// WARNING: This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. + +import { LoadContext, SaveContext } from "../context"; +import { TokenUsage } from "../model/token-usage"; +import { TraceTime } from "./trace-time"; + +export class TraceSpan { + static readonly shorthandProperty: string | undefined = undefined; + + name: string = ""; + __time!: TraceTime; + signature?: string | undefined; + inputs?: Record | undefined; + output?: unknown | undefined; + error?: string | undefined; + __usage?: TokenUsage | undefined; + attributes?: Record | undefined; + __frames?: unknown[] = []; + + constructor(init?: Partial) { + this.name = init?.name ?? ""; + if (init?.__time !== undefined) { + this.__time = init.__time; + } + if (init?.signature !== undefined) { + this.signature = init.signature; + } + if (init?.inputs !== undefined) { + this.inputs = init.inputs; + } + if (init?.output !== undefined) { + this.output = init.output; + } + if (init?.error !== undefined) { + this.error = init.error; + } + if (init?.__usage !== undefined) { + this.__usage = init.__usage; + } + if (init?.attributes !== undefined) { + this.attributes = init.attributes; + } + if (init?.__frames !== undefined) { + this.__frames = init.__frames; + } + } + + //#region Load Methods + + static load(data: Record, context?: LoadContext): TraceSpan { + if (context) { + data = context.processInput(data) as Record; + } + + const instance = new TraceSpan(); + + if (data["name"] !== undefined && data["name"] !== null) { + instance.name = String(data["name"]); + } + if (data["__time"] !== undefined && data["__time"] !== null) { + instance.__time = TraceTime.load(data["__time"] as Record, context); + } + if (data["signature"] !== undefined && data["signature"] !== null) { + instance.signature = String(data["signature"]); + } + if (data["inputs"] !== undefined && data["inputs"] !== null) { + instance.inputs = data["inputs"] as Record; + } + if (data["output"] !== undefined && data["output"] !== null) { + instance.output = data["output"] as unknown; + } + if (data["error"] !== undefined && data["error"] !== null) { + instance.error = String(data["error"]); + } + if (data["__usage"] !== undefined && data["__usage"] !== null) { + instance.__usage = TokenUsage.load(data["__usage"] as Record, context); + } + if (data["attributes"] !== undefined && data["attributes"] !== null) { + instance.attributes = data["attributes"] as Record; + } + if (data["__frames"] !== undefined && data["__frames"] !== null) { + instance.__frames = data["__frames"] as unknown[]; + } + + if (context) { + return context.processOutput(instance) as TraceSpan; + } + return instance; + } + + //#endregion + + //#region Save Methods + + save(context?: SaveContext): Record { + let obj: this = this; + if (context) { + obj = context.processObject(obj) as this; + } + + const result: Record = {}; + + if (obj.name !== undefined && obj.name !== null) { + result["name"] = obj.name; + } + if (obj.__time !== undefined && obj.__time !== null) { + result["__time"] = obj.__time.save(context); + } + if (obj.signature !== undefined && obj.signature !== null) { + result["signature"] = obj.signature; + } + if (obj.inputs !== undefined && obj.inputs !== null) { + result["inputs"] = obj.inputs; + } + if (obj.output !== undefined && obj.output !== null) { + result["output"] = obj.output; + } + if (obj.error !== undefined && obj.error !== null) { + result["error"] = obj.error; + } + if (obj.__usage !== undefined && obj.__usage !== null) { + result["__usage"] = obj.__usage.save(context); + } + if (obj.attributes !== undefined && obj.attributes !== null) { + result["attributes"] = obj.attributes; + } + if (obj.__frames !== undefined && obj.__frames !== null) { + result["__frames"] = obj.__frames; + } + + if (context) { + return context.processDict(result); + } + return result; + } + + toYaml(context?: SaveContext): string { + context = context ?? new SaveContext(); + return context.toYaml(this.save(context)); + } + + toJson(context?: SaveContext, indent: number = 2): string { + context = context ?? new SaveContext(); + return context.toJson(this.save(context), indent); + } + + static fromJson(json: string, context?: LoadContext): TraceSpan { + const data = JSON.parse(json); + return TraceSpan.load(data as Record, context); + } + + static fromYaml(yaml: string, context?: LoadContext): TraceSpan { + const { parse } = require("yaml"); + const data = parse(yaml); + return TraceSpan.load(data as Record, context); + } + + //#endregion +} + diff --git a/runtime/typescript/packages/core/src/model/tracing/trace-time.ts b/runtime/typescript/packages/core/src/model/tracing/trace-time.ts new file mode 100644 index 00000000..fbfa3733 --- /dev/null +++ b/runtime/typescript/packages/core/src/model/tracing/trace-time.ts @@ -0,0 +1,95 @@ +// Copyright (c) Microsoft. All rights reserved. +// WARNING: This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. + +import { LoadContext, SaveContext } from "../context"; + +export class TraceTime { + static readonly shorthandProperty: string | undefined = undefined; + + start: string = ""; + end: string = ""; + duration: number = 0; + + constructor(init?: Partial) { + this.start = init?.start ?? ""; + this.end = init?.end ?? ""; + this.duration = init?.duration ?? 0; + } + + //#region Load Methods + + static load(data: Record, context?: LoadContext): TraceTime { + if (context) { + data = context.processInput(data) as Record; + } + + const instance = new TraceTime(); + + if (data["start"] !== undefined && data["start"] !== null) { + instance.start = String(data["start"]); + } + if (data["end"] !== undefined && data["end"] !== null) { + instance.end = String(data["end"]); + } + if (data["duration"] !== undefined && data["duration"] !== null) { + instance.duration = Number(data["duration"]); + } + + if (context) { + return context.processOutput(instance) as TraceTime; + } + return instance; + } + + //#endregion + + //#region Save Methods + + save(context?: SaveContext): Record { + let obj: this = this; + if (context) { + obj = context.processObject(obj) as this; + } + + const result: Record = {}; + + if (obj.start !== undefined && obj.start !== null) { + result["start"] = obj.start; + } + if (obj.end !== undefined && obj.end !== null) { + result["end"] = obj.end; + } + if (obj.duration !== undefined && obj.duration !== null) { + result["duration"] = obj.duration; + } + + if (context) { + return context.processDict(result); + } + return result; + } + + toYaml(context?: SaveContext): string { + context = context ?? new SaveContext(); + return context.toYaml(this.save(context)); + } + + toJson(context?: SaveContext, indent: number = 2): string { + context = context ?? new SaveContext(); + return context.toJson(this.save(context), indent); + } + + static fromJson(json: string, context?: LoadContext): TraceTime { + const data = JSON.parse(json); + return TraceTime.load(data as Record, context); + } + + static fromYaml(yaml: string, context?: LoadContext): TraceTime { + const { parse } = require("yaml"); + const data = parse(yaml); + return TraceTime.load(data as Record, context); + } + + //#endregion +} + diff --git a/runtime/typescript/packages/core/src/model/wire/anthropic-image-block.ts b/runtime/typescript/packages/core/src/model/wire/anthropic-image-block.ts new file mode 100644 index 00000000..a1ebaed8 --- /dev/null +++ b/runtime/typescript/packages/core/src/model/wire/anthropic-image-block.ts @@ -0,0 +1,90 @@ +// Copyright (c) Microsoft. All rights reserved. +// WARNING: This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. + +import { LoadContext, SaveContext } from "../context"; +import { AnthropicImageSource } from "./anthropic-image-source"; + +export class AnthropicImageBlock { + static readonly shorthandProperty: string | undefined = undefined; + + type: string = "image"; + source!: AnthropicImageSource; + + constructor(init?: Partial) { + this.type = init?.type ?? "image"; + if (init?.source !== undefined) { + this.source = init.source; + } + } + + //#region Load Methods + + static load(data: Record, context?: LoadContext): AnthropicImageBlock { + if (context) { + data = context.processInput(data) as Record; + } + + const instance = new AnthropicImageBlock(); + + if (data["type"] !== undefined && data["type"] !== null) { + instance.type = String(data["type"]); + } + if (data["source"] !== undefined && data["source"] !== null) { + instance.source = AnthropicImageSource.load(data["source"] as Record, context); + } + + if (context) { + return context.processOutput(instance) as AnthropicImageBlock; + } + return instance; + } + + //#endregion + + //#region Save Methods + + save(context?: SaveContext): Record { + let obj: this = this; + if (context) { + obj = context.processObject(obj) as this; + } + + const result: Record = {}; + + if (obj.type !== undefined && obj.type !== null) { + result["type"] = obj.type; + } + if (obj.source !== undefined && obj.source !== null) { + result["source"] = obj.source.save(context); + } + + if (context) { + return context.processDict(result); + } + return result; + } + + toYaml(context?: SaveContext): string { + context = context ?? new SaveContext(); + return context.toYaml(this.save(context)); + } + + toJson(context?: SaveContext, indent: number = 2): string { + context = context ?? new SaveContext(); + return context.toJson(this.save(context), indent); + } + + static fromJson(json: string, context?: LoadContext): AnthropicImageBlock { + const data = JSON.parse(json); + return AnthropicImageBlock.load(data as Record, context); + } + + static fromYaml(yaml: string, context?: LoadContext): AnthropicImageBlock { + const { parse } = require("yaml"); + const data = parse(yaml); + return AnthropicImageBlock.load(data as Record, context); + } + + //#endregion +} + diff --git a/runtime/typescript/packages/core/src/model/wire/anthropic-image-source.ts b/runtime/typescript/packages/core/src/model/wire/anthropic-image-source.ts new file mode 100644 index 00000000..8d993a47 --- /dev/null +++ b/runtime/typescript/packages/core/src/model/wire/anthropic-image-source.ts @@ -0,0 +1,95 @@ +// Copyright (c) Microsoft. All rights reserved. +// WARNING: This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. + +import { LoadContext, SaveContext } from "../context"; + +export class AnthropicImageSource { + static readonly shorthandProperty: string | undefined = undefined; + + type: string = "base64"; + media_type: string = ""; + data: string = ""; + + constructor(init?: Partial) { + this.type = init?.type ?? "base64"; + this.media_type = init?.media_type ?? ""; + this.data = init?.data ?? ""; + } + + //#region Load Methods + + static load(data: Record, context?: LoadContext): AnthropicImageSource { + if (context) { + data = context.processInput(data) as Record; + } + + const instance = new AnthropicImageSource(); + + if (data["type"] !== undefined && data["type"] !== null) { + instance.type = String(data["type"]); + } + if (data["media_type"] !== undefined && data["media_type"] !== null) { + instance.media_type = String(data["media_type"]); + } + if (data["data"] !== undefined && data["data"] !== null) { + instance.data = String(data["data"]); + } + + if (context) { + return context.processOutput(instance) as AnthropicImageSource; + } + return instance; + } + + //#endregion + + //#region Save Methods + + save(context?: SaveContext): Record { + let obj: this = this; + if (context) { + obj = context.processObject(obj) as this; + } + + const result: Record = {}; + + if (obj.type !== undefined && obj.type !== null) { + result["type"] = obj.type; + } + if (obj.media_type !== undefined && obj.media_type !== null) { + result["media_type"] = obj.media_type; + } + if (obj.data !== undefined && obj.data !== null) { + result["data"] = obj.data; + } + + if (context) { + return context.processDict(result); + } + return result; + } + + toYaml(context?: SaveContext): string { + context = context ?? new SaveContext(); + return context.toYaml(this.save(context)); + } + + toJson(context?: SaveContext, indent: number = 2): string { + context = context ?? new SaveContext(); + return context.toJson(this.save(context), indent); + } + + static fromJson(json: string, context?: LoadContext): AnthropicImageSource { + const data = JSON.parse(json); + return AnthropicImageSource.load(data as Record, context); + } + + static fromYaml(yaml: string, context?: LoadContext): AnthropicImageSource { + const { parse } = require("yaml"); + const data = parse(yaml); + return AnthropicImageSource.load(data as Record, context); + } + + //#endregion +} + diff --git a/runtime/typescript/packages/core/src/model/wire/anthropic-messages-request.ts b/runtime/typescript/packages/core/src/model/wire/anthropic-messages-request.ts new file mode 100644 index 00000000..a6eacbdc --- /dev/null +++ b/runtime/typescript/packages/core/src/model/wire/anthropic-messages-request.ts @@ -0,0 +1,232 @@ +// Copyright (c) Microsoft. All rights reserved. +// WARNING: This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. + +import { LoadContext, SaveContext } from "../context"; +import { AnthropicToolDefinition } from "./anthropic-tool-definition"; +import { AnthropicWireMessage } from "./anthropic-wire-message"; + +export class AnthropicMessagesRequest { + static readonly shorthandProperty: string | undefined = undefined; + + model: string = ""; + messages: AnthropicWireMessage[] = []; + max_tokens: number = 0; + system?: string | undefined; + temperature?: number | undefined; + top_p?: number | undefined; + top_k?: number | undefined; + stop_sequences?: string[] = []; + tools?: AnthropicToolDefinition[] = []; + + constructor(init?: Partial) { + this.model = init?.model ?? ""; + this.messages = init?.messages ?? []; + this.max_tokens = init?.max_tokens ?? 0; + if (init?.system !== undefined) { + this.system = init.system; + } + if (init?.temperature !== undefined) { + this.temperature = init.temperature; + } + if (init?.top_p !== undefined) { + this.top_p = init.top_p; + } + if (init?.top_k !== undefined) { + this.top_k = init.top_k; + } + if (init?.stop_sequences !== undefined) { + this.stop_sequences = init.stop_sequences; + } + if (init?.tools !== undefined) { + this.tools = init.tools; + } + } + + //#region Load Methods + + static load(data: Record, context?: LoadContext): AnthropicMessagesRequest { + if (context) { + data = context.processInput(data) as Record; + } + + const instance = new AnthropicMessagesRequest(); + + if (data["model"] !== undefined && data["model"] !== null) { + instance.model = String(data["model"]); + } + if (data["messages"] !== undefined && data["messages"] !== null) { + instance.messages = AnthropicMessagesRequest.loadMessages(data["messages"] as unknown[], context); + } + if (data["max_tokens"] !== undefined && data["max_tokens"] !== null) { + instance.max_tokens = Number(data["max_tokens"]); + } + if (data["system"] !== undefined && data["system"] !== null) { + instance.system = String(data["system"]); + } + if (data["temperature"] !== undefined && data["temperature"] !== null) { + instance.temperature = Number(data["temperature"]); + } + if (data["top_p"] !== undefined && data["top_p"] !== null) { + instance.top_p = Number(data["top_p"]); + } + if (data["top_k"] !== undefined && data["top_k"] !== null) { + instance.top_k = Number(data["top_k"]); + } + if (data["stop_sequences"] !== undefined && data["stop_sequences"] !== null) { + instance.stop_sequences = (data["stop_sequences"] as unknown[]).map(v => String(v)); + } + if (data["tools"] !== undefined && data["tools"] !== null) { + instance.tools = AnthropicMessagesRequest.loadTools(data["tools"] as unknown[], context); + } + + if (context) { + return context.processOutput(instance) as AnthropicMessagesRequest; + } + return instance; + } + + static loadMessages(data: Record[] | unknown[], context?: LoadContext): AnthropicWireMessage[] { + if (!Array.isArray(data)) { + // Convert dict/object format to array format + const result: Record[] = []; + for (const [k, v] of Object.entries(data)) { + if (typeof v === "object" && v !== null && !Array.isArray(v)) { + result.push({ name: k, ...(v as Record) }); + } else { + result.push({ name: k, "role": v }); + } + } + data = result; + } + return data.map(item => AnthropicWireMessage.load(item as Record, context)); + } + + static saveMessages(items: AnthropicWireMessage[], context?: SaveContext): Record[] | Record { + if (!context) { + context = new SaveContext(); + } + + // This type doesn't have a 'name' property, so always use array format + return items.map(item => item.save(context)); + } + + static loadTools(data: Record[] | unknown[], context?: LoadContext): AnthropicToolDefinition[] { + if (!Array.isArray(data)) { + // Convert dict/object format to array format + const result: Record[] = []; + for (const [k, v] of Object.entries(data)) { + if (typeof v === "object" && v !== null && !Array.isArray(v)) { + result.push({ name: k, ...(v as Record) }); + } else { + result.push({ name: k, "description": v }); + } + } + data = result; + } + return data.map(item => AnthropicToolDefinition.load(item as Record, context)); + } + + static saveTools(items: AnthropicToolDefinition[], context?: SaveContext): Record[] | Record { + if (!context) { + context = new SaveContext(); + } + + if (context.collectionFormat === "array") { + return items.map(item => item.save(context)); + } + + // Object format: use name as key + const result: Record = {}; + for (const item of items) { + const itemData = item.save(context) as Record; + const name = itemData["name"] as string | undefined; + delete itemData["name"]; + if (name) { + // Check if we can use shorthand (only primary property set) + const shorthand = (item.constructor as typeof AnthropicToolDefinition).shorthandProperty; + if (context.useShorthand && shorthand && Object.keys(itemData).length === 1 && shorthand in itemData) { + result[name] = itemData[shorthand]; + continue; + } + result[name] = itemData; + } else { + // No name, fall back to array format for this item + if (!result["_unnamed"]) { + result["_unnamed"] = []; + } + (result["_unnamed"] as unknown[]).push(itemData); + } + } + return result; + } + + //#endregion + + //#region Save Methods + + save(context?: SaveContext): Record { + let obj: this = this; + if (context) { + obj = context.processObject(obj) as this; + } + + const result: Record = {}; + + if (obj.model !== undefined && obj.model !== null) { + result["model"] = obj.model; + } + if (obj.messages !== undefined && obj.messages !== null) { + result["messages"] = AnthropicMessagesRequest.saveMessages(obj.messages, context); + } + if (obj.max_tokens !== undefined && obj.max_tokens !== null) { + result["max_tokens"] = obj.max_tokens; + } + if (obj.system !== undefined && obj.system !== null) { + result["system"] = obj.system; + } + if (obj.temperature !== undefined && obj.temperature !== null) { + result["temperature"] = obj.temperature; + } + if (obj.top_p !== undefined && obj.top_p !== null) { + result["top_p"] = obj.top_p; + } + if (obj.top_k !== undefined && obj.top_k !== null) { + result["top_k"] = obj.top_k; + } + if (obj.stop_sequences !== undefined && obj.stop_sequences !== null) { + result["stop_sequences"] = obj.stop_sequences; + } + if (obj.tools !== undefined && obj.tools !== null) { + result["tools"] = AnthropicMessagesRequest.saveTools(obj.tools, context); + } + + if (context) { + return context.processDict(result); + } + return result; + } + + toYaml(context?: SaveContext): string { + context = context ?? new SaveContext(); + return context.toYaml(this.save(context)); + } + + toJson(context?: SaveContext, indent: number = 2): string { + context = context ?? new SaveContext(); + return context.toJson(this.save(context), indent); + } + + static fromJson(json: string, context?: LoadContext): AnthropicMessagesRequest { + const data = JSON.parse(json); + return AnthropicMessagesRequest.load(data as Record, context); + } + + static fromYaml(yaml: string, context?: LoadContext): AnthropicMessagesRequest { + const { parse } = require("yaml"); + const data = parse(yaml); + return AnthropicMessagesRequest.load(data as Record, context); + } + + //#endregion +} + diff --git a/runtime/typescript/packages/core/src/model/wire/anthropic-messages-response.ts b/runtime/typescript/packages/core/src/model/wire/anthropic-messages-response.ts new file mode 100644 index 00000000..7f6f5259 --- /dev/null +++ b/runtime/typescript/packages/core/src/model/wire/anthropic-messages-response.ts @@ -0,0 +1,130 @@ +// Copyright (c) Microsoft. All rights reserved. +// WARNING: This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. + +import { LoadContext, SaveContext } from "../context"; +import { AnthropicUsage } from "./anthropic-usage"; + +export class AnthropicMessagesResponse { + static readonly shorthandProperty: string | undefined = undefined; + + id: string = ""; + type: string = "message"; + role: string = "assistant"; + content: unknown[] = []; + model: string = ""; + stop_reason: string = ""; + usage!: AnthropicUsage; + + constructor(init?: Partial) { + this.id = init?.id ?? ""; + this.type = init?.type ?? "message"; + this.role = init?.role ?? "assistant"; + this.content = init?.content ?? []; + this.model = init?.model ?? ""; + this.stop_reason = init?.stop_reason ?? ""; + if (init?.usage !== undefined) { + this.usage = init.usage; + } + } + + //#region Load Methods + + static load(data: Record, context?: LoadContext): AnthropicMessagesResponse { + if (context) { + data = context.processInput(data) as Record; + } + + const instance = new AnthropicMessagesResponse(); + + if (data["id"] !== undefined && data["id"] !== null) { + instance.id = String(data["id"]); + } + if (data["type"] !== undefined && data["type"] !== null) { + instance.type = String(data["type"]); + } + if (data["role"] !== undefined && data["role"] !== null) { + instance.role = String(data["role"]); + } + if (data["content"] !== undefined && data["content"] !== null) { + instance.content = data["content"] as unknown[]; + } + if (data["model"] !== undefined && data["model"] !== null) { + instance.model = String(data["model"]); + } + if (data["stop_reason"] !== undefined && data["stop_reason"] !== null) { + instance.stop_reason = String(data["stop_reason"]); + } + if (data["usage"] !== undefined && data["usage"] !== null) { + instance.usage = AnthropicUsage.load(data["usage"] as Record, context); + } + + if (context) { + return context.processOutput(instance) as AnthropicMessagesResponse; + } + return instance; + } + + //#endregion + + //#region Save Methods + + save(context?: SaveContext): Record { + let obj: this = this; + if (context) { + obj = context.processObject(obj) as this; + } + + const result: Record = {}; + + if (obj.id !== undefined && obj.id !== null) { + result["id"] = obj.id; + } + if (obj.type !== undefined && obj.type !== null) { + result["type"] = obj.type; + } + if (obj.role !== undefined && obj.role !== null) { + result["role"] = obj.role; + } + if (obj.content !== undefined && obj.content !== null) { + result["content"] = obj.content; + } + if (obj.model !== undefined && obj.model !== null) { + result["model"] = obj.model; + } + if (obj.stop_reason !== undefined && obj.stop_reason !== null) { + result["stop_reason"] = obj.stop_reason; + } + if (obj.usage !== undefined && obj.usage !== null) { + result["usage"] = obj.usage.save(context); + } + + if (context) { + return context.processDict(result); + } + return result; + } + + toYaml(context?: SaveContext): string { + context = context ?? new SaveContext(); + return context.toYaml(this.save(context)); + } + + toJson(context?: SaveContext, indent: number = 2): string { + context = context ?? new SaveContext(); + return context.toJson(this.save(context), indent); + } + + static fromJson(json: string, context?: LoadContext): AnthropicMessagesResponse { + const data = JSON.parse(json); + return AnthropicMessagesResponse.load(data as Record, context); + } + + static fromYaml(yaml: string, context?: LoadContext): AnthropicMessagesResponse { + const { parse } = require("yaml"); + const data = parse(yaml); + return AnthropicMessagesResponse.load(data as Record, context); + } + + //#endregion +} + diff --git a/runtime/typescript/packages/core/src/model/wire/anthropic-text-block.ts b/runtime/typescript/packages/core/src/model/wire/anthropic-text-block.ts new file mode 100644 index 00000000..cca223ab --- /dev/null +++ b/runtime/typescript/packages/core/src/model/wire/anthropic-text-block.ts @@ -0,0 +1,87 @@ +// Copyright (c) Microsoft. All rights reserved. +// WARNING: This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. + +import { LoadContext, SaveContext } from "../context"; + +export class AnthropicTextBlock { + static readonly shorthandProperty: string | undefined = undefined; + + type: string = "text"; + text: string = ""; + + constructor(init?: Partial) { + this.type = init?.type ?? "text"; + this.text = init?.text ?? ""; + } + + //#region Load Methods + + static load(data: Record, context?: LoadContext): AnthropicTextBlock { + if (context) { + data = context.processInput(data) as Record; + } + + const instance = new AnthropicTextBlock(); + + if (data["type"] !== undefined && data["type"] !== null) { + instance.type = String(data["type"]); + } + if (data["text"] !== undefined && data["text"] !== null) { + instance.text = String(data["text"]); + } + + if (context) { + return context.processOutput(instance) as AnthropicTextBlock; + } + return instance; + } + + //#endregion + + //#region Save Methods + + save(context?: SaveContext): Record { + let obj: this = this; + if (context) { + obj = context.processObject(obj) as this; + } + + const result: Record = {}; + + if (obj.type !== undefined && obj.type !== null) { + result["type"] = obj.type; + } + if (obj.text !== undefined && obj.text !== null) { + result["text"] = obj.text; + } + + if (context) { + return context.processDict(result); + } + return result; + } + + toYaml(context?: SaveContext): string { + context = context ?? new SaveContext(); + return context.toYaml(this.save(context)); + } + + toJson(context?: SaveContext, indent: number = 2): string { + context = context ?? new SaveContext(); + return context.toJson(this.save(context), indent); + } + + static fromJson(json: string, context?: LoadContext): AnthropicTextBlock { + const data = JSON.parse(json); + return AnthropicTextBlock.load(data as Record, context); + } + + static fromYaml(yaml: string, context?: LoadContext): AnthropicTextBlock { + const { parse } = require("yaml"); + const data = parse(yaml); + return AnthropicTextBlock.load(data as Record, context); + } + + //#endregion +} + diff --git a/runtime/typescript/packages/core/src/model/wire/anthropic-tool-definition.ts b/runtime/typescript/packages/core/src/model/wire/anthropic-tool-definition.ts new file mode 100644 index 00000000..b75ff750 --- /dev/null +++ b/runtime/typescript/packages/core/src/model/wire/anthropic-tool-definition.ts @@ -0,0 +1,97 @@ +// Copyright (c) Microsoft. All rights reserved. +// WARNING: This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. + +import { LoadContext, SaveContext } from "../context"; + +export class AnthropicToolDefinition { + static readonly shorthandProperty: string | undefined = undefined; + + name: string = ""; + description?: string | undefined; + input_schema: Record = {}; + + constructor(init?: Partial) { + this.name = init?.name ?? ""; + if (init?.description !== undefined) { + this.description = init.description; + } + this.input_schema = init?.input_schema ?? {}; + } + + //#region Load Methods + + static load(data: Record, context?: LoadContext): AnthropicToolDefinition { + if (context) { + data = context.processInput(data) as Record; + } + + const instance = new AnthropicToolDefinition(); + + if (data["name"] !== undefined && data["name"] !== null) { + instance.name = String(data["name"]); + } + if (data["description"] !== undefined && data["description"] !== null) { + instance.description = String(data["description"]); + } + if (data["input_schema"] !== undefined && data["input_schema"] !== null) { + instance.input_schema = data["input_schema"] as Record; + } + + if (context) { + return context.processOutput(instance) as AnthropicToolDefinition; + } + return instance; + } + + //#endregion + + //#region Save Methods + + save(context?: SaveContext): Record { + let obj: this = this; + if (context) { + obj = context.processObject(obj) as this; + } + + const result: Record = {}; + + if (obj.name !== undefined && obj.name !== null) { + result["name"] = obj.name; + } + if (obj.description !== undefined && obj.description !== null) { + result["description"] = obj.description; + } + if (obj.input_schema !== undefined && obj.input_schema !== null) { + result["input_schema"] = obj.input_schema; + } + + if (context) { + return context.processDict(result); + } + return result; + } + + toYaml(context?: SaveContext): string { + context = context ?? new SaveContext(); + return context.toYaml(this.save(context)); + } + + toJson(context?: SaveContext, indent: number = 2): string { + context = context ?? new SaveContext(); + return context.toJson(this.save(context), indent); + } + + static fromJson(json: string, context?: LoadContext): AnthropicToolDefinition { + const data = JSON.parse(json); + return AnthropicToolDefinition.load(data as Record, context); + } + + static fromYaml(yaml: string, context?: LoadContext): AnthropicToolDefinition { + const { parse } = require("yaml"); + const data = parse(yaml); + return AnthropicToolDefinition.load(data as Record, context); + } + + //#endregion +} + diff --git a/runtime/typescript/packages/core/src/model/wire/anthropic-tool-result-block.ts b/runtime/typescript/packages/core/src/model/wire/anthropic-tool-result-block.ts new file mode 100644 index 00000000..70044759 --- /dev/null +++ b/runtime/typescript/packages/core/src/model/wire/anthropic-tool-result-block.ts @@ -0,0 +1,95 @@ +// Copyright (c) Microsoft. All rights reserved. +// WARNING: This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. + +import { LoadContext, SaveContext } from "../context"; + +export class AnthropicToolResultBlock { + static readonly shorthandProperty: string | undefined = undefined; + + type: string = "tool_result"; + tool_use_id: string = ""; + content: string = ""; + + constructor(init?: Partial) { + this.type = init?.type ?? "tool_result"; + this.tool_use_id = init?.tool_use_id ?? ""; + this.content = init?.content ?? ""; + } + + //#region Load Methods + + static load(data: Record, context?: LoadContext): AnthropicToolResultBlock { + if (context) { + data = context.processInput(data) as Record; + } + + const instance = new AnthropicToolResultBlock(); + + if (data["type"] !== undefined && data["type"] !== null) { + instance.type = String(data["type"]); + } + if (data["tool_use_id"] !== undefined && data["tool_use_id"] !== null) { + instance.tool_use_id = String(data["tool_use_id"]); + } + if (data["content"] !== undefined && data["content"] !== null) { + instance.content = String(data["content"]); + } + + if (context) { + return context.processOutput(instance) as AnthropicToolResultBlock; + } + return instance; + } + + //#endregion + + //#region Save Methods + + save(context?: SaveContext): Record { + let obj: this = this; + if (context) { + obj = context.processObject(obj) as this; + } + + const result: Record = {}; + + if (obj.type !== undefined && obj.type !== null) { + result["type"] = obj.type; + } + if (obj.tool_use_id !== undefined && obj.tool_use_id !== null) { + result["tool_use_id"] = obj.tool_use_id; + } + if (obj.content !== undefined && obj.content !== null) { + result["content"] = obj.content; + } + + if (context) { + return context.processDict(result); + } + return result; + } + + toYaml(context?: SaveContext): string { + context = context ?? new SaveContext(); + return context.toYaml(this.save(context)); + } + + toJson(context?: SaveContext, indent: number = 2): string { + context = context ?? new SaveContext(); + return context.toJson(this.save(context), indent); + } + + static fromJson(json: string, context?: LoadContext): AnthropicToolResultBlock { + const data = JSON.parse(json); + return AnthropicToolResultBlock.load(data as Record, context); + } + + static fromYaml(yaml: string, context?: LoadContext): AnthropicToolResultBlock { + const { parse } = require("yaml"); + const data = parse(yaml); + return AnthropicToolResultBlock.load(data as Record, context); + } + + //#endregion +} + diff --git a/runtime/typescript/packages/core/src/model/wire/anthropic-tool-use-block.ts b/runtime/typescript/packages/core/src/model/wire/anthropic-tool-use-block.ts new file mode 100644 index 00000000..8f0ba806 --- /dev/null +++ b/runtime/typescript/packages/core/src/model/wire/anthropic-tool-use-block.ts @@ -0,0 +1,103 @@ +// Copyright (c) Microsoft. All rights reserved. +// WARNING: This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. + +import { LoadContext, SaveContext } from "../context"; + +export class AnthropicToolUseBlock { + static readonly shorthandProperty: string | undefined = undefined; + + type: string = "tool_use"; + id: string = ""; + name: string = ""; + input: Record = {}; + + constructor(init?: Partial) { + this.type = init?.type ?? "tool_use"; + this.id = init?.id ?? ""; + this.name = init?.name ?? ""; + this.input = init?.input ?? {}; + } + + //#region Load Methods + + static load(data: Record, context?: LoadContext): AnthropicToolUseBlock { + if (context) { + data = context.processInput(data) as Record; + } + + const instance = new AnthropicToolUseBlock(); + + if (data["type"] !== undefined && data["type"] !== null) { + instance.type = String(data["type"]); + } + if (data["id"] !== undefined && data["id"] !== null) { + instance.id = String(data["id"]); + } + if (data["name"] !== undefined && data["name"] !== null) { + instance.name = String(data["name"]); + } + if (data["input"] !== undefined && data["input"] !== null) { + instance.input = data["input"] as Record; + } + + if (context) { + return context.processOutput(instance) as AnthropicToolUseBlock; + } + return instance; + } + + //#endregion + + //#region Save Methods + + save(context?: SaveContext): Record { + let obj: this = this; + if (context) { + obj = context.processObject(obj) as this; + } + + const result: Record = {}; + + if (obj.type !== undefined && obj.type !== null) { + result["type"] = obj.type; + } + if (obj.id !== undefined && obj.id !== null) { + result["id"] = obj.id; + } + if (obj.name !== undefined && obj.name !== null) { + result["name"] = obj.name; + } + if (obj.input !== undefined && obj.input !== null) { + result["input"] = obj.input; + } + + if (context) { + return context.processDict(result); + } + return result; + } + + toYaml(context?: SaveContext): string { + context = context ?? new SaveContext(); + return context.toYaml(this.save(context)); + } + + toJson(context?: SaveContext, indent: number = 2): string { + context = context ?? new SaveContext(); + return context.toJson(this.save(context), indent); + } + + static fromJson(json: string, context?: LoadContext): AnthropicToolUseBlock { + const data = JSON.parse(json); + return AnthropicToolUseBlock.load(data as Record, context); + } + + static fromYaml(yaml: string, context?: LoadContext): AnthropicToolUseBlock { + const { parse } = require("yaml"); + const data = parse(yaml); + return AnthropicToolUseBlock.load(data as Record, context); + } + + //#endregion +} + diff --git a/runtime/typescript/packages/core/src/model/wire/anthropic-usage.ts b/runtime/typescript/packages/core/src/model/wire/anthropic-usage.ts new file mode 100644 index 00000000..20156e9e --- /dev/null +++ b/runtime/typescript/packages/core/src/model/wire/anthropic-usage.ts @@ -0,0 +1,87 @@ +// Copyright (c) Microsoft. All rights reserved. +// WARNING: This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. + +import { LoadContext, SaveContext } from "../context"; + +export class AnthropicUsage { + static readonly shorthandProperty: string | undefined = undefined; + + input_tokens: number = 0; + output_tokens: number = 0; + + constructor(init?: Partial) { + this.input_tokens = init?.input_tokens ?? 0; + this.output_tokens = init?.output_tokens ?? 0; + } + + //#region Load Methods + + static load(data: Record, context?: LoadContext): AnthropicUsage { + if (context) { + data = context.processInput(data) as Record; + } + + const instance = new AnthropicUsage(); + + if (data["input_tokens"] !== undefined && data["input_tokens"] !== null) { + instance.input_tokens = Number(data["input_tokens"]); + } + if (data["output_tokens"] !== undefined && data["output_tokens"] !== null) { + instance.output_tokens = Number(data["output_tokens"]); + } + + if (context) { + return context.processOutput(instance) as AnthropicUsage; + } + return instance; + } + + //#endregion + + //#region Save Methods + + save(context?: SaveContext): Record { + let obj: this = this; + if (context) { + obj = context.processObject(obj) as this; + } + + const result: Record = {}; + + if (obj.input_tokens !== undefined && obj.input_tokens !== null) { + result["input_tokens"] = obj.input_tokens; + } + if (obj.output_tokens !== undefined && obj.output_tokens !== null) { + result["output_tokens"] = obj.output_tokens; + } + + if (context) { + return context.processDict(result); + } + return result; + } + + toYaml(context?: SaveContext): string { + context = context ?? new SaveContext(); + return context.toYaml(this.save(context)); + } + + toJson(context?: SaveContext, indent: number = 2): string { + context = context ?? new SaveContext(); + return context.toJson(this.save(context), indent); + } + + static fromJson(json: string, context?: LoadContext): AnthropicUsage { + const data = JSON.parse(json); + return AnthropicUsage.load(data as Record, context); + } + + static fromYaml(yaml: string, context?: LoadContext): AnthropicUsage { + const { parse } = require("yaml"); + const data = parse(yaml); + return AnthropicUsage.load(data as Record, context); + } + + //#endregion +} + diff --git a/runtime/typescript/packages/core/src/model/wire/anthropic-wire-message.ts b/runtime/typescript/packages/core/src/model/wire/anthropic-wire-message.ts new file mode 100644 index 00000000..7a097bfd --- /dev/null +++ b/runtime/typescript/packages/core/src/model/wire/anthropic-wire-message.ts @@ -0,0 +1,87 @@ +// Copyright (c) Microsoft. All rights reserved. +// WARNING: This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. + +import { LoadContext, SaveContext } from "../context"; + +export class AnthropicWireMessage { + static readonly shorthandProperty: string | undefined = undefined; + + role: string = ""; + content: unknown[] = []; + + constructor(init?: Partial) { + this.role = init?.role ?? ""; + this.content = init?.content ?? []; + } + + //#region Load Methods + + static load(data: Record, context?: LoadContext): AnthropicWireMessage { + if (context) { + data = context.processInput(data) as Record; + } + + const instance = new AnthropicWireMessage(); + + if (data["role"] !== undefined && data["role"] !== null) { + instance.role = String(data["role"]); + } + if (data["content"] !== undefined && data["content"] !== null) { + instance.content = data["content"] as unknown[]; + } + + if (context) { + return context.processOutput(instance) as AnthropicWireMessage; + } + return instance; + } + + //#endregion + + //#region Save Methods + + save(context?: SaveContext): Record { + let obj: this = this; + if (context) { + obj = context.processObject(obj) as this; + } + + const result: Record = {}; + + if (obj.role !== undefined && obj.role !== null) { + result["role"] = obj.role; + } + if (obj.content !== undefined && obj.content !== null) { + result["content"] = obj.content; + } + + if (context) { + return context.processDict(result); + } + return result; + } + + toYaml(context?: SaveContext): string { + context = context ?? new SaveContext(); + return context.toYaml(this.save(context)); + } + + toJson(context?: SaveContext, indent: number = 2): string { + context = context ?? new SaveContext(); + return context.toJson(this.save(context), indent); + } + + static fromJson(json: string, context?: LoadContext): AnthropicWireMessage { + const data = JSON.parse(json); + return AnthropicWireMessage.load(data as Record, context); + } + + static fromYaml(yaml: string, context?: LoadContext): AnthropicWireMessage { + const { parse } = require("yaml"); + const data = parse(yaml); + return AnthropicWireMessage.load(data as Record, context); + } + + //#endregion +} + diff --git a/runtime/typescript/packages/core/src/model/wire/index.ts b/runtime/typescript/packages/core/src/model/wire/index.ts new file mode 100644 index 00000000..8fdcc319 --- /dev/null +++ b/runtime/typescript/packages/core/src/model/wire/index.ts @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft. All rights reserved. +// WARNING: This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. + +export { AnthropicTextBlock } from "./anthropic-text-block"; +export { AnthropicImageSource } from "./anthropic-image-source"; +export { AnthropicImageBlock } from "./anthropic-image-block"; +export { AnthropicToolUseBlock } from "./anthropic-tool-use-block"; +export { AnthropicToolResultBlock } from "./anthropic-tool-result-block"; +export { AnthropicWireMessage } from "./anthropic-wire-message"; +export { AnthropicToolDefinition } from "./anthropic-tool-definition"; +export { AnthropicMessagesRequest } from "./anthropic-messages-request"; +export { AnthropicUsage } from "./anthropic-usage"; +export { AnthropicMessagesResponse } from "./anthropic-messages-response"; diff --git a/runtime/typescript/packages/core/tests/bindings.test.ts b/runtime/typescript/packages/core/tests/bindings.test.ts index 2c741b2f..da9e6a1f 100644 --- a/runtime/typescript/packages/core/tests/bindings.test.ts +++ b/runtime/typescript/packages/core/tests/bindings.test.ts @@ -10,9 +10,7 @@ import { } from "../src/core/registry.js"; import { Message, text } from "../src/core/types.js"; import { Prompty } from "@prompty/core"; -import { Tool } from "../src/model/index.js"; -import { Binding } from "../src/model/binding.js"; -import { Property } from "../src/model/property.js"; +import { Tool, Binding, Property } from "../src/model/index.js"; import type { Renderer, Parser, Executor, Processor } from "../src/core/interfaces.js"; // --------------------------------------------------------------------------- diff --git a/runtime/typescript/packages/core/tests/model/core/file-not-found-error.test.ts b/runtime/typescript/packages/core/tests/model/core/file-not-found-error.test.ts new file mode 100644 index 00000000..c108c5c5 --- /dev/null +++ b/runtime/typescript/packages/core/tests/model/core/file-not-found-error.test.ts @@ -0,0 +1,71 @@ +// Copyright (c) Microsoft. All rights reserved. +// WARNING: This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. + +import { FileNotFoundError } from "../../../src/model/index"; + +describe("FileNotFoundError", () => { + describe("construction", () => { + it("should create a new instance with defaults", () => { + const instance = new FileNotFoundError(); + expect(instance).toBeDefined(); + }); + + it("should create a new instance with partial initialization", () => { + const instance = new FileNotFoundError({}); + expect(instance).toBeDefined(); + }); + }); + + describe("JSON serialization", () => { + it("should load from JSON - example 1", () => { + const json = `{\n "message": "Prompty file not found: ./chat.prompty",\n "path": "./chat.prompty"\n}`; + const instance = FileNotFoundError.fromJson(json); + expect(instance).toBeDefined(); + expect(instance.message).toEqual("Prompty file not found: ./chat.prompty"); + expect(instance.path).toEqual("./chat.prompty"); + }); + + it("should round-trip JSON - example 1", () => { + const json = `{\n "message": "Prompty file not found: ./chat.prompty",\n "path": "./chat.prompty"\n}`; + const instance = FileNotFoundError.fromJson(json); + const output = instance.toJson(); + const reloaded = FileNotFoundError.fromJson(output); + expect(reloaded.message).toEqual(instance.message); + expect(reloaded.path).toEqual(instance.path); + }); + }); + + describe("YAML serialization", () => { + it("should load from YAML - example 1", () => { + const yaml = `message: "Prompty file not found: ./chat.prompty"\npath: ./chat.prompty\n`; + const instance = FileNotFoundError.fromYaml(yaml); + expect(instance).toBeDefined(); + expect(instance.message).toEqual("Prompty file not found: ./chat.prompty"); + expect(instance.path).toEqual("./chat.prompty"); + }); + + it("should round-trip YAML - example 1", () => { + const yaml = `message: "Prompty file not found: ./chat.prompty"\npath: ./chat.prompty\n`; + const instance = FileNotFoundError.fromYaml(yaml); + const output = instance.toYaml(); + const reloaded = FileNotFoundError.fromYaml(output); + expect(reloaded.message).toEqual(instance.message); + expect(reloaded.path).toEqual(instance.path); + }); + }); + + describe("load and save", () => { + it("should load from dictionary", () => { + const data: Record = {}; + const instance = FileNotFoundError.load(data); + expect(instance).toBeDefined(); + }); + + it("should save to dictionary", () => { + const instance = new FileNotFoundError(); + const data = instance.save(); + expect(data).toBeDefined(); + expect(typeof data).toBe("object"); + }); + }); +}); diff --git a/runtime/typescript/packages/core/tests/model/core/invoker-error.test.ts b/runtime/typescript/packages/core/tests/model/core/invoker-error.test.ts new file mode 100644 index 00000000..5e5b2cff --- /dev/null +++ b/runtime/typescript/packages/core/tests/model/core/invoker-error.test.ts @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft. All rights reserved. +// WARNING: This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. + +import { InvokerError } from "../../../src/model/index"; + +describe("InvokerError", () => { + describe("construction", () => { + it("should create a new instance with defaults", () => { + const instance = new InvokerError(); + expect(instance).toBeDefined(); + }); + + it("should create a new instance with partial initialization", () => { + const instance = new InvokerError({}); + expect(instance).toBeDefined(); + }); + }); + + describe("JSON serialization", () => { + it("should load from JSON - example 1", () => { + const json = `{\n "message": "No renderer registered for key: jinja2",\n "component": "renderer",\n "key": "jinja2"\n}`; + const instance = InvokerError.fromJson(json); + expect(instance).toBeDefined(); + expect(instance.message).toEqual("No renderer registered for key: jinja2"); + expect(instance.component).toEqual("renderer"); + expect(instance.key).toEqual("jinja2"); + }); + + it("should round-trip JSON - example 1", () => { + const json = `{\n "message": "No renderer registered for key: jinja2",\n "component": "renderer",\n "key": "jinja2"\n}`; + const instance = InvokerError.fromJson(json); + const output = instance.toJson(); + const reloaded = InvokerError.fromJson(output); + expect(reloaded.message).toEqual(instance.message); + expect(reloaded.component).toEqual(instance.component); + expect(reloaded.key).toEqual(instance.key); + }); + }); + + describe("YAML serialization", () => { + it("should load from YAML - example 1", () => { + const yaml = `message: "No renderer registered for key: jinja2"\ncomponent: renderer\nkey: jinja2\n`; + const instance = InvokerError.fromYaml(yaml); + expect(instance).toBeDefined(); + expect(instance.message).toEqual("No renderer registered for key: jinja2"); + expect(instance.component).toEqual("renderer"); + expect(instance.key).toEqual("jinja2"); + }); + + it("should round-trip YAML - example 1", () => { + const yaml = `message: "No renderer registered for key: jinja2"\ncomponent: renderer\nkey: jinja2\n`; + const instance = InvokerError.fromYaml(yaml); + const output = instance.toYaml(); + const reloaded = InvokerError.fromYaml(output); + expect(reloaded.message).toEqual(instance.message); + expect(reloaded.component).toEqual(instance.component); + expect(reloaded.key).toEqual(instance.key); + }); + }); + + describe("load and save", () => { + it("should load from dictionary", () => { + const data: Record = {}; + const instance = InvokerError.load(data); + expect(instance).toBeDefined(); + }); + + it("should save to dictionary", () => { + const instance = new InvokerError(); + const data = instance.save(); + expect(data).toBeDefined(); + expect(typeof data).toBe("object"); + }); + }); +}); diff --git a/runtime/typescript/packages/core/tests/model/core/validation-error.test.ts b/runtime/typescript/packages/core/tests/model/core/validation-error.test.ts new file mode 100644 index 00000000..0ac7ef99 --- /dev/null +++ b/runtime/typescript/packages/core/tests/model/core/validation-error.test.ts @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft. All rights reserved. +// WARNING: This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. + +import { ValidationError } from "../../../src/model/index"; + +describe("ValidationError", () => { + describe("construction", () => { + it("should create a new instance with defaults", () => { + const instance = new ValidationError(); + expect(instance).toBeDefined(); + }); + + it("should create a new instance with partial initialization", () => { + const instance = new ValidationError({}); + expect(instance).toBeDefined(); + }); + }); + + describe("JSON serialization", () => { + it("should load from JSON - example 1", () => { + const json = `{\n "message": "Missing required input: firstName",\n "property": "firstName",\n "constraint": "required"\n}`; + const instance = ValidationError.fromJson(json); + expect(instance).toBeDefined(); + expect(instance.message).toEqual("Missing required input: firstName"); + expect(instance.property).toEqual("firstName"); + expect(instance.constraint).toEqual("required"); + }); + + it("should round-trip JSON - example 1", () => { + const json = `{\n "message": "Missing required input: firstName",\n "property": "firstName",\n "constraint": "required"\n}`; + const instance = ValidationError.fromJson(json); + const output = instance.toJson(); + const reloaded = ValidationError.fromJson(output); + expect(reloaded.message).toEqual(instance.message); + expect(reloaded.property).toEqual(instance.property); + expect(reloaded.constraint).toEqual(instance.constraint); + }); + }); + + describe("YAML serialization", () => { + it("should load from YAML - example 1", () => { + const yaml = `message: "Missing required input: firstName"\nproperty: firstName\nconstraint: required\n`; + const instance = ValidationError.fromYaml(yaml); + expect(instance).toBeDefined(); + expect(instance.message).toEqual("Missing required input: firstName"); + expect(instance.property).toEqual("firstName"); + expect(instance.constraint).toEqual("required"); + }); + + it("should round-trip YAML - example 1", () => { + const yaml = `message: "Missing required input: firstName"\nproperty: firstName\nconstraint: required\n`; + const instance = ValidationError.fromYaml(yaml); + const output = instance.toYaml(); + const reloaded = ValidationError.fromYaml(output); + expect(reloaded.message).toEqual(instance.message); + expect(reloaded.property).toEqual(instance.property); + expect(reloaded.constraint).toEqual(instance.constraint); + }); + }); + + describe("load and save", () => { + it("should load from dictionary", () => { + const data: Record = {}; + const instance = ValidationError.load(data); + expect(instance).toBeDefined(); + }); + + it("should save to dictionary", () => { + const instance = new ValidationError(); + const data = instance.save(); + expect(data).toBeDefined(); + expect(typeof data).toBe("object"); + }); + }); +}); diff --git a/runtime/typescript/packages/core/tests/model/core/validation-result.test.ts b/runtime/typescript/packages/core/tests/model/core/validation-result.test.ts new file mode 100644 index 00000000..539cb2dd --- /dev/null +++ b/runtime/typescript/packages/core/tests/model/core/validation-result.test.ts @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft. All rights reserved. +// WARNING: This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. + +import { ValidationResult } from "../../../src/model/index"; + +describe("ValidationResult", () => { + describe("construction", () => { + it("should create a new instance with defaults", () => { + const instance = new ValidationResult(); + expect(instance).toBeDefined(); + }); + + it("should create a new instance with partial initialization", () => { + const instance = new ValidationResult({}); + expect(instance).toBeDefined(); + }); + }); + + describe("JSON serialization", () => { + it("should load from JSON - example 1", () => { + const json = `{\n "valid": true,\n "errors": []\n}`; + const instance = ValidationResult.fromJson(json); + expect(instance).toBeDefined(); + expect(instance.valid).toEqual(true); + }); + + it("should round-trip JSON - example 1", () => { + const json = `{\n "valid": true,\n "errors": []\n}`; + const instance = ValidationResult.fromJson(json); + const output = instance.toJson(); + const reloaded = ValidationResult.fromJson(output); + expect(reloaded.valid).toEqual(instance.valid); + }); + }); + + describe("YAML serialization", () => { + it("should load from YAML - example 1", () => { + const yaml = `valid: true\nerrors: []\n`; + const instance = ValidationResult.fromYaml(yaml); + expect(instance).toBeDefined(); + expect(instance.valid).toEqual(true); + }); + + it("should round-trip YAML - example 1", () => { + const yaml = `valid: true\nerrors: []\n`; + const instance = ValidationResult.fromYaml(yaml); + const output = instance.toYaml(); + const reloaded = ValidationResult.fromYaml(output); + expect(reloaded.valid).toEqual(instance.valid); + }); + }); + + describe("load and save", () => { + it("should load from dictionary", () => { + const data: Record = {}; + const instance = ValidationResult.load(data); + expect(instance).toBeDefined(); + }); + + it("should save to dictionary", () => { + const instance = new ValidationResult(); + const data = instance.save(); + expect(data).toBeDefined(); + expect(typeof data).toBe("object"); + }); + }); +}); diff --git a/runtime/typescript/packages/core/tests/model/streaming/stream-options.test.ts b/runtime/typescript/packages/core/tests/model/streaming/stream-options.test.ts new file mode 100644 index 00000000..ba97ce57 --- /dev/null +++ b/runtime/typescript/packages/core/tests/model/streaming/stream-options.test.ts @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft. All rights reserved. +// WARNING: This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. + +import { StreamOptions } from "../../../src/model/index"; + +describe("StreamOptions", () => { + describe("construction", () => { + it("should create a new instance with defaults", () => { + const instance = new StreamOptions(); + expect(instance).toBeDefined(); + }); + + it("should create a new instance with partial initialization", () => { + const instance = new StreamOptions({}); + expect(instance).toBeDefined(); + }); + }); + + describe("JSON serialization", () => { + it("should load from JSON - example 1", () => { + const json = `{\n "includeUsage": true\n}`; + const instance = StreamOptions.fromJson(json); + expect(instance).toBeDefined(); + expect(instance.includeUsage).toEqual(true); + }); + + it("should round-trip JSON - example 1", () => { + const json = `{\n "includeUsage": true\n}`; + const instance = StreamOptions.fromJson(json); + const output = instance.toJson(); + const reloaded = StreamOptions.fromJson(output); + expect(reloaded.includeUsage).toEqual(instance.includeUsage); + }); + }); + + describe("YAML serialization", () => { + it("should load from YAML - example 1", () => { + const yaml = `includeUsage: true\n`; + const instance = StreamOptions.fromYaml(yaml); + expect(instance).toBeDefined(); + expect(instance.includeUsage).toEqual(true); + }); + + it("should round-trip YAML - example 1", () => { + const yaml = `includeUsage: true\n`; + const instance = StreamOptions.fromYaml(yaml); + const output = instance.toYaml(); + const reloaded = StreamOptions.fromYaml(output); + expect(reloaded.includeUsage).toEqual(instance.includeUsage); + }); + }); + + describe("load and save", () => { + it("should load from dictionary", () => { + const data: Record = {}; + const instance = StreamOptions.load(data); + expect(instance).toBeDefined(); + }); + + it("should save to dictionary", () => { + const instance = new StreamOptions(); + const data = instance.save(); + expect(data).toBeDefined(); + expect(typeof data).toBe("object"); + }); + }); +}); diff --git a/runtime/typescript/packages/core/tests/model/tracing/trace-file.test.ts b/runtime/typescript/packages/core/tests/model/tracing/trace-file.test.ts new file mode 100644 index 00000000..9aaa2d26 --- /dev/null +++ b/runtime/typescript/packages/core/tests/model/tracing/trace-file.test.ts @@ -0,0 +1,71 @@ +// Copyright (c) Microsoft. All rights reserved. +// WARNING: This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. + +import { TraceFile } from "../../../src/model/index"; + +describe("TraceFile", () => { + describe("construction", () => { + it("should create a new instance with defaults", () => { + const instance = new TraceFile(); + expect(instance).toBeDefined(); + }); + + it("should create a new instance with partial initialization", () => { + const instance = new TraceFile({}); + expect(instance).toBeDefined(); + }); + }); + + describe("JSON serialization", () => { + it("should load from JSON - example 1", () => { + const json = `{\n "runtime": "python",\n "version": "2.0.0"\n}`; + const instance = TraceFile.fromJson(json); + expect(instance).toBeDefined(); + expect(instance.runtime).toEqual("python"); + expect(instance.version).toEqual("2.0.0"); + }); + + it("should round-trip JSON - example 1", () => { + const json = `{\n "runtime": "python",\n "version": "2.0.0"\n}`; + const instance = TraceFile.fromJson(json); + const output = instance.toJson(); + const reloaded = TraceFile.fromJson(output); + expect(reloaded.runtime).toEqual(instance.runtime); + expect(reloaded.version).toEqual(instance.version); + }); + }); + + describe("YAML serialization", () => { + it("should load from YAML - example 1", () => { + const yaml = `runtime: python\nversion: 2.0.0\n`; + const instance = TraceFile.fromYaml(yaml); + expect(instance).toBeDefined(); + expect(instance.runtime).toEqual("python"); + expect(instance.version).toEqual("2.0.0"); + }); + + it("should round-trip YAML - example 1", () => { + const yaml = `runtime: python\nversion: 2.0.0\n`; + const instance = TraceFile.fromYaml(yaml); + const output = instance.toYaml(); + const reloaded = TraceFile.fromYaml(output); + expect(reloaded.runtime).toEqual(instance.runtime); + expect(reloaded.version).toEqual(instance.version); + }); + }); + + describe("load and save", () => { + it("should load from dictionary", () => { + const data: Record = {}; + const instance = TraceFile.load(data); + expect(instance).toBeDefined(); + }); + + it("should save to dictionary", () => { + const instance = new TraceFile(); + const data = instance.save(); + expect(data).toBeDefined(); + expect(typeof data).toBe("object"); + }); + }); +}); diff --git a/runtime/typescript/packages/core/tests/model/tracing/trace-span.test.ts b/runtime/typescript/packages/core/tests/model/tracing/trace-span.test.ts new file mode 100644 index 00000000..0a5c0fdb --- /dev/null +++ b/runtime/typescript/packages/core/tests/model/tracing/trace-span.test.ts @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft. All rights reserved. +// WARNING: This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. + +import { TraceSpan } from "../../../src/model/index"; + +describe("TraceSpan", () => { + describe("construction", () => { + it("should create a new instance with defaults", () => { + const instance = new TraceSpan(); + expect(instance).toBeDefined(); + }); + + it("should create a new instance with partial initialization", () => { + const instance = new TraceSpan({}); + expect(instance).toBeDefined(); + }); + }); + + describe("JSON serialization", () => { + it("should load from JSON - example 1", () => { + const json = `{\n "name": "prompty.core.pipeline.run",\n "signature": "prompty.core.pipeline.run",\n "error": "Connection refused"\n}`; + const instance = TraceSpan.fromJson(json); + expect(instance).toBeDefined(); + expect(instance.name).toEqual("prompty.core.pipeline.run"); + expect(instance.signature).toEqual("prompty.core.pipeline.run"); + expect(instance.error).toEqual("Connection refused"); + }); + + it("should round-trip JSON - example 1", () => { + const json = `{\n "name": "prompty.core.pipeline.run",\n "signature": "prompty.core.pipeline.run",\n "error": "Connection refused"\n}`; + const instance = TraceSpan.fromJson(json); + const output = instance.toJson(); + const reloaded = TraceSpan.fromJson(output); + expect(reloaded.name).toEqual(instance.name); + expect(reloaded.signature).toEqual(instance.signature); + expect(reloaded.error).toEqual(instance.error); + }); + }); + + describe("YAML serialization", () => { + it("should load from YAML - example 1", () => { + const yaml = `name: prompty.core.pipeline.run\nsignature: prompty.core.pipeline.run\nerror: Connection refused\n`; + const instance = TraceSpan.fromYaml(yaml); + expect(instance).toBeDefined(); + expect(instance.name).toEqual("prompty.core.pipeline.run"); + expect(instance.signature).toEqual("prompty.core.pipeline.run"); + expect(instance.error).toEqual("Connection refused"); + }); + + it("should round-trip YAML - example 1", () => { + const yaml = `name: prompty.core.pipeline.run\nsignature: prompty.core.pipeline.run\nerror: Connection refused\n`; + const instance = TraceSpan.fromYaml(yaml); + const output = instance.toYaml(); + const reloaded = TraceSpan.fromYaml(output); + expect(reloaded.name).toEqual(instance.name); + expect(reloaded.signature).toEqual(instance.signature); + expect(reloaded.error).toEqual(instance.error); + }); + }); + + describe("load and save", () => { + it("should load from dictionary", () => { + const data: Record = {}; + const instance = TraceSpan.load(data); + expect(instance).toBeDefined(); + }); + + it("should save to dictionary", () => { + const instance = new TraceSpan(); + const data = instance.save(); + expect(data).toBeDefined(); + expect(typeof data).toBe("object"); + }); + }); +}); diff --git a/runtime/typescript/packages/core/tests/model/tracing/trace-time.test.ts b/runtime/typescript/packages/core/tests/model/tracing/trace-time.test.ts new file mode 100644 index 00000000..2ba66ff9 --- /dev/null +++ b/runtime/typescript/packages/core/tests/model/tracing/trace-time.test.ts @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft. All rights reserved. +// WARNING: This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. + +import { TraceTime } from "../../../src/model/index"; + +describe("TraceTime", () => { + describe("construction", () => { + it("should create a new instance with defaults", () => { + const instance = new TraceTime(); + expect(instance).toBeDefined(); + }); + + it("should create a new instance with partial initialization", () => { + const instance = new TraceTime({}); + expect(instance).toBeDefined(); + }); + }); + + describe("JSON serialization", () => { + it("should load from JSON - example 1", () => { + const json = `{\n "start": "2026-04-04T12:00:00Z",\n "end": "2026-04-04T12:00:01Z",\n "duration": 1000\n}`; + const instance = TraceTime.fromJson(json); + expect(instance).toBeDefined(); + expect(instance.start).toEqual("2026-04-04T12:00:00Z"); + expect(instance.end).toEqual("2026-04-04T12:00:01Z"); + expect(instance.duration).toEqual(1000); + }); + + it("should round-trip JSON - example 1", () => { + const json = `{\n "start": "2026-04-04T12:00:00Z",\n "end": "2026-04-04T12:00:01Z",\n "duration": 1000\n}`; + const instance = TraceTime.fromJson(json); + const output = instance.toJson(); + const reloaded = TraceTime.fromJson(output); + expect(reloaded.start).toEqual(instance.start); + expect(reloaded.end).toEqual(instance.end); + expect(reloaded.duration).toEqual(instance.duration); + }); + }); + + describe("YAML serialization", () => { + it("should load from YAML - example 1", () => { + const yaml = `start: "2026-04-04T12:00:00Z"\nend: "2026-04-04T12:00:01Z"\nduration: 1000\n`; + const instance = TraceTime.fromYaml(yaml); + expect(instance).toBeDefined(); + expect(instance.start).toEqual("2026-04-04T12:00:00Z"); + expect(instance.end).toEqual("2026-04-04T12:00:01Z"); + expect(instance.duration).toEqual(1000); + }); + + it("should round-trip YAML - example 1", () => { + const yaml = `start: "2026-04-04T12:00:00Z"\nend: "2026-04-04T12:00:01Z"\nduration: 1000\n`; + const instance = TraceTime.fromYaml(yaml); + const output = instance.toYaml(); + const reloaded = TraceTime.fromYaml(output); + expect(reloaded.start).toEqual(instance.start); + expect(reloaded.end).toEqual(instance.end); + expect(reloaded.duration).toEqual(instance.duration); + }); + }); + + describe("load and save", () => { + it("should load from dictionary", () => { + const data: Record = {}; + const instance = TraceTime.load(data); + expect(instance).toBeDefined(); + }); + + it("should save to dictionary", () => { + const instance = new TraceTime(); + const data = instance.save(); + expect(data).toBeDefined(); + expect(typeof data).toBe("object"); + }); + }); +}); diff --git a/runtime/typescript/packages/core/tests/model/wire/anthropic-image-block.test.ts b/runtime/typescript/packages/core/tests/model/wire/anthropic-image-block.test.ts new file mode 100644 index 00000000..22c37f56 --- /dev/null +++ b/runtime/typescript/packages/core/tests/model/wire/anthropic-image-block.test.ts @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft. All rights reserved. +// WARNING: This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. + +import { AnthropicImageBlock } from "../../../src/model/index"; + +describe("AnthropicImageBlock", () => { + describe("construction", () => { + it("should create a new instance with defaults", () => { + const instance = new AnthropicImageBlock(); + expect(instance).toBeDefined(); + }); + + it("should create a new instance with partial initialization", () => { + const instance = new AnthropicImageBlock({}); + expect(instance).toBeDefined(); + }); + }); + + describe("load and save", () => { + it("should load from dictionary", () => { + const data: Record = {}; + const instance = AnthropicImageBlock.load(data); + expect(instance).toBeDefined(); + }); + + it("should save to dictionary", () => { + const instance = new AnthropicImageBlock(); + const data = instance.save(); + expect(data).toBeDefined(); + expect(typeof data).toBe("object"); + }); + }); +}); diff --git a/runtime/typescript/packages/core/tests/model/wire/anthropic-image-source.test.ts b/runtime/typescript/packages/core/tests/model/wire/anthropic-image-source.test.ts new file mode 100644 index 00000000..c1a3f877 --- /dev/null +++ b/runtime/typescript/packages/core/tests/model/wire/anthropic-image-source.test.ts @@ -0,0 +1,71 @@ +// Copyright (c) Microsoft. All rights reserved. +// WARNING: This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. + +import { AnthropicImageSource } from "../../../src/model/index"; + +describe("AnthropicImageSource", () => { + describe("construction", () => { + it("should create a new instance with defaults", () => { + const instance = new AnthropicImageSource(); + expect(instance).toBeDefined(); + }); + + it("should create a new instance with partial initialization", () => { + const instance = new AnthropicImageSource({}); + expect(instance).toBeDefined(); + }); + }); + + describe("JSON serialization", () => { + it("should load from JSON - example 1", () => { + const json = `{\n "media_type": "image/png",\n "data": "iVBORw0KGgo..."\n}`; + const instance = AnthropicImageSource.fromJson(json); + expect(instance).toBeDefined(); + expect(instance.media_type).toEqual("image/png"); + expect(instance.data).toEqual("iVBORw0KGgo..."); + }); + + it("should round-trip JSON - example 1", () => { + const json = `{\n "media_type": "image/png",\n "data": "iVBORw0KGgo..."\n}`; + const instance = AnthropicImageSource.fromJson(json); + const output = instance.toJson(); + const reloaded = AnthropicImageSource.fromJson(output); + expect(reloaded.media_type).toEqual(instance.media_type); + expect(reloaded.data).toEqual(instance.data); + }); + }); + + describe("YAML serialization", () => { + it("should load from YAML - example 1", () => { + const yaml = `media_type: image/png\ndata: iVBORw0KGgo...\n`; + const instance = AnthropicImageSource.fromYaml(yaml); + expect(instance).toBeDefined(); + expect(instance.media_type).toEqual("image/png"); + expect(instance.data).toEqual("iVBORw0KGgo..."); + }); + + it("should round-trip YAML - example 1", () => { + const yaml = `media_type: image/png\ndata: iVBORw0KGgo...\n`; + const instance = AnthropicImageSource.fromYaml(yaml); + const output = instance.toYaml(); + const reloaded = AnthropicImageSource.fromYaml(output); + expect(reloaded.media_type).toEqual(instance.media_type); + expect(reloaded.data).toEqual(instance.data); + }); + }); + + describe("load and save", () => { + it("should load from dictionary", () => { + const data: Record = {}; + const instance = AnthropicImageSource.load(data); + expect(instance).toBeDefined(); + }); + + it("should save to dictionary", () => { + const instance = new AnthropicImageSource(); + const data = instance.save(); + expect(data).toBeDefined(); + expect(typeof data).toBe("object"); + }); + }); +}); diff --git a/runtime/typescript/packages/core/tests/model/wire/anthropic-messages-request.test.ts b/runtime/typescript/packages/core/tests/model/wire/anthropic-messages-request.test.ts new file mode 100644 index 00000000..1bd34776 --- /dev/null +++ b/runtime/typescript/packages/core/tests/model/wire/anthropic-messages-request.test.ts @@ -0,0 +1,87 @@ +// Copyright (c) Microsoft. All rights reserved. +// WARNING: This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. + +import { AnthropicMessagesRequest } from "../../../src/model/index"; + +describe("AnthropicMessagesRequest", () => { + describe("construction", () => { + it("should create a new instance with defaults", () => { + const instance = new AnthropicMessagesRequest(); + expect(instance).toBeDefined(); + }); + + it("should create a new instance with partial initialization", () => { + const instance = new AnthropicMessagesRequest({}); + expect(instance).toBeDefined(); + }); + }); + + describe("JSON serialization", () => { + it("should load from JSON - example 1", () => { + const json = `{\n "model": "claude-sonnet-4-20250514",\n "max_tokens": 4096,\n "system": "You are a helpful assistant.",\n "temperature": 0.7,\n "top_p": 0.9,\n "top_k": 40,\n "stop_sequences": [\n "\\n\\nHuman:"\n ]\n}`; + const instance = AnthropicMessagesRequest.fromJson(json); + expect(instance).toBeDefined(); + expect(instance.model).toEqual("claude-sonnet-4-20250514"); + expect(instance.max_tokens).toEqual(4096); + expect(instance.system).toEqual("You are a helpful assistant."); + expect(instance.temperature).toEqual(0.7); + expect(instance.top_p).toEqual(0.9); + expect(instance.top_k).toEqual(40); + }); + + it("should round-trip JSON - example 1", () => { + const json = `{\n "model": "claude-sonnet-4-20250514",\n "max_tokens": 4096,\n "system": "You are a helpful assistant.",\n "temperature": 0.7,\n "top_p": 0.9,\n "top_k": 40,\n "stop_sequences": [\n "\\n\\nHuman:"\n ]\n}`; + const instance = AnthropicMessagesRequest.fromJson(json); + const output = instance.toJson(); + const reloaded = AnthropicMessagesRequest.fromJson(output); + expect(reloaded.model).toEqual(instance.model); + expect(reloaded.max_tokens).toEqual(instance.max_tokens); + expect(reloaded.system).toEqual(instance.system); + expect(reloaded.temperature).toEqual(instance.temperature); + expect(reloaded.top_p).toEqual(instance.top_p); + expect(reloaded.top_k).toEqual(instance.top_k); + }); + }); + + describe("YAML serialization", () => { + it("should load from YAML - example 1", () => { + const yaml = `model: claude-sonnet-4-20250514\nmax_tokens: 4096\nsystem: You are a helpful assistant.\ntemperature: 0.7\ntop_p: 0.9\ntop_k: 40\nstop_sequences:\n - "\\n\\nHuman:"\n`; + const instance = AnthropicMessagesRequest.fromYaml(yaml); + expect(instance).toBeDefined(); + expect(instance.model).toEqual("claude-sonnet-4-20250514"); + expect(instance.max_tokens).toEqual(4096); + expect(instance.system).toEqual("You are a helpful assistant."); + expect(instance.temperature).toEqual(0.7); + expect(instance.top_p).toEqual(0.9); + expect(instance.top_k).toEqual(40); + }); + + it("should round-trip YAML - example 1", () => { + const yaml = `model: claude-sonnet-4-20250514\nmax_tokens: 4096\nsystem: You are a helpful assistant.\ntemperature: 0.7\ntop_p: 0.9\ntop_k: 40\nstop_sequences:\n - "\\n\\nHuman:"\n`; + const instance = AnthropicMessagesRequest.fromYaml(yaml); + const output = instance.toYaml(); + const reloaded = AnthropicMessagesRequest.fromYaml(output); + expect(reloaded.model).toEqual(instance.model); + expect(reloaded.max_tokens).toEqual(instance.max_tokens); + expect(reloaded.system).toEqual(instance.system); + expect(reloaded.temperature).toEqual(instance.temperature); + expect(reloaded.top_p).toEqual(instance.top_p); + expect(reloaded.top_k).toEqual(instance.top_k); + }); + }); + + describe("load and save", () => { + it("should load from dictionary", () => { + const data: Record = {}; + const instance = AnthropicMessagesRequest.load(data); + expect(instance).toBeDefined(); + }); + + it("should save to dictionary", () => { + const instance = new AnthropicMessagesRequest(); + const data = instance.save(); + expect(data).toBeDefined(); + expect(typeof data).toBe("object"); + }); + }); +}); diff --git a/runtime/typescript/packages/core/tests/model/wire/anthropic-messages-response.test.ts b/runtime/typescript/packages/core/tests/model/wire/anthropic-messages-response.test.ts new file mode 100644 index 00000000..4a5522cd --- /dev/null +++ b/runtime/typescript/packages/core/tests/model/wire/anthropic-messages-response.test.ts @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft. All rights reserved. +// WARNING: This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. + +import { AnthropicMessagesResponse } from "../../../src/model/index"; + +describe("AnthropicMessagesResponse", () => { + describe("construction", () => { + it("should create a new instance with defaults", () => { + const instance = new AnthropicMessagesResponse(); + expect(instance).toBeDefined(); + }); + + it("should create a new instance with partial initialization", () => { + const instance = new AnthropicMessagesResponse({}); + expect(instance).toBeDefined(); + }); + }); + + describe("JSON serialization", () => { + it("should load from JSON - example 1", () => { + const json = `{\n "id": "msg_01XFDUDYJgAACzvnptvVoYEL",\n "model": "claude-sonnet-4-20250514",\n "stop_reason": "end_turn"\n}`; + const instance = AnthropicMessagesResponse.fromJson(json); + expect(instance).toBeDefined(); + expect(instance.id).toEqual("msg_01XFDUDYJgAACzvnptvVoYEL"); + expect(instance.model).toEqual("claude-sonnet-4-20250514"); + expect(instance.stop_reason).toEqual("end_turn"); + }); + + it("should round-trip JSON - example 1", () => { + const json = `{\n "id": "msg_01XFDUDYJgAACzvnptvVoYEL",\n "model": "claude-sonnet-4-20250514",\n "stop_reason": "end_turn"\n}`; + const instance = AnthropicMessagesResponse.fromJson(json); + const output = instance.toJson(); + const reloaded = AnthropicMessagesResponse.fromJson(output); + expect(reloaded.id).toEqual(instance.id); + expect(reloaded.model).toEqual(instance.model); + expect(reloaded.stop_reason).toEqual(instance.stop_reason); + }); + }); + + describe("YAML serialization", () => { + it("should load from YAML - example 1", () => { + const yaml = `id: msg_01XFDUDYJgAACzvnptvVoYEL\nmodel: claude-sonnet-4-20250514\nstop_reason: end_turn\n`; + const instance = AnthropicMessagesResponse.fromYaml(yaml); + expect(instance).toBeDefined(); + expect(instance.id).toEqual("msg_01XFDUDYJgAACzvnptvVoYEL"); + expect(instance.model).toEqual("claude-sonnet-4-20250514"); + expect(instance.stop_reason).toEqual("end_turn"); + }); + + it("should round-trip YAML - example 1", () => { + const yaml = `id: msg_01XFDUDYJgAACzvnptvVoYEL\nmodel: claude-sonnet-4-20250514\nstop_reason: end_turn\n`; + const instance = AnthropicMessagesResponse.fromYaml(yaml); + const output = instance.toYaml(); + const reloaded = AnthropicMessagesResponse.fromYaml(output); + expect(reloaded.id).toEqual(instance.id); + expect(reloaded.model).toEqual(instance.model); + expect(reloaded.stop_reason).toEqual(instance.stop_reason); + }); + }); + + describe("load and save", () => { + it("should load from dictionary", () => { + const data: Record = {}; + const instance = AnthropicMessagesResponse.load(data); + expect(instance).toBeDefined(); + }); + + it("should save to dictionary", () => { + const instance = new AnthropicMessagesResponse(); + const data = instance.save(); + expect(data).toBeDefined(); + expect(typeof data).toBe("object"); + }); + }); +}); diff --git a/runtime/typescript/packages/core/tests/model/wire/anthropic-text-block.test.ts b/runtime/typescript/packages/core/tests/model/wire/anthropic-text-block.test.ts new file mode 100644 index 00000000..f15860ef --- /dev/null +++ b/runtime/typescript/packages/core/tests/model/wire/anthropic-text-block.test.ts @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft. All rights reserved. +// WARNING: This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. + +import { AnthropicTextBlock } from "../../../src/model/index"; + +describe("AnthropicTextBlock", () => { + describe("construction", () => { + it("should create a new instance with defaults", () => { + const instance = new AnthropicTextBlock(); + expect(instance).toBeDefined(); + }); + + it("should create a new instance with partial initialization", () => { + const instance = new AnthropicTextBlock({}); + expect(instance).toBeDefined(); + }); + }); + + describe("JSON serialization", () => { + it("should load from JSON - example 1", () => { + const json = `{\n "text": "Hello, how can I help?"\n}`; + const instance = AnthropicTextBlock.fromJson(json); + expect(instance).toBeDefined(); + expect(instance.text).toEqual("Hello, how can I help?"); + }); + + it("should round-trip JSON - example 1", () => { + const json = `{\n "text": "Hello, how can I help?"\n}`; + const instance = AnthropicTextBlock.fromJson(json); + const output = instance.toJson(); + const reloaded = AnthropicTextBlock.fromJson(output); + expect(reloaded.text).toEqual(instance.text); + }); + }); + + describe("YAML serialization", () => { + it("should load from YAML - example 1", () => { + const yaml = `text: Hello, how can I help?\n`; + const instance = AnthropicTextBlock.fromYaml(yaml); + expect(instance).toBeDefined(); + expect(instance.text).toEqual("Hello, how can I help?"); + }); + + it("should round-trip YAML - example 1", () => { + const yaml = `text: Hello, how can I help?\n`; + const instance = AnthropicTextBlock.fromYaml(yaml); + const output = instance.toYaml(); + const reloaded = AnthropicTextBlock.fromYaml(output); + expect(reloaded.text).toEqual(instance.text); + }); + }); + + describe("load and save", () => { + it("should load from dictionary", () => { + const data: Record = {}; + const instance = AnthropicTextBlock.load(data); + expect(instance).toBeDefined(); + }); + + it("should save to dictionary", () => { + const instance = new AnthropicTextBlock(); + const data = instance.save(); + expect(data).toBeDefined(); + expect(typeof data).toBe("object"); + }); + }); +}); diff --git a/runtime/typescript/packages/core/tests/model/wire/anthropic-tool-definition.test.ts b/runtime/typescript/packages/core/tests/model/wire/anthropic-tool-definition.test.ts new file mode 100644 index 00000000..cd75dd31 --- /dev/null +++ b/runtime/typescript/packages/core/tests/model/wire/anthropic-tool-definition.test.ts @@ -0,0 +1,71 @@ +// Copyright (c) Microsoft. All rights reserved. +// WARNING: This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. + +import { AnthropicToolDefinition } from "../../../src/model/index"; + +describe("AnthropicToolDefinition", () => { + describe("construction", () => { + it("should create a new instance with defaults", () => { + const instance = new AnthropicToolDefinition(); + expect(instance).toBeDefined(); + }); + + it("should create a new instance with partial initialization", () => { + const instance = new AnthropicToolDefinition({}); + expect(instance).toBeDefined(); + }); + }); + + describe("JSON serialization", () => { + it("should load from JSON - example 1", () => { + const json = `{\n "name": "get_weather",\n "description": "Get the current weather for a city"\n}`; + const instance = AnthropicToolDefinition.fromJson(json); + expect(instance).toBeDefined(); + expect(instance.name).toEqual("get_weather"); + expect(instance.description).toEqual("Get the current weather for a city"); + }); + + it("should round-trip JSON - example 1", () => { + const json = `{\n "name": "get_weather",\n "description": "Get the current weather for a city"\n}`; + const instance = AnthropicToolDefinition.fromJson(json); + const output = instance.toJson(); + const reloaded = AnthropicToolDefinition.fromJson(output); + expect(reloaded.name).toEqual(instance.name); + expect(reloaded.description).toEqual(instance.description); + }); + }); + + describe("YAML serialization", () => { + it("should load from YAML - example 1", () => { + const yaml = `name: get_weather\ndescription: Get the current weather for a city\n`; + const instance = AnthropicToolDefinition.fromYaml(yaml); + expect(instance).toBeDefined(); + expect(instance.name).toEqual("get_weather"); + expect(instance.description).toEqual("Get the current weather for a city"); + }); + + it("should round-trip YAML - example 1", () => { + const yaml = `name: get_weather\ndescription: Get the current weather for a city\n`; + const instance = AnthropicToolDefinition.fromYaml(yaml); + const output = instance.toYaml(); + const reloaded = AnthropicToolDefinition.fromYaml(output); + expect(reloaded.name).toEqual(instance.name); + expect(reloaded.description).toEqual(instance.description); + }); + }); + + describe("load and save", () => { + it("should load from dictionary", () => { + const data: Record = {}; + const instance = AnthropicToolDefinition.load(data); + expect(instance).toBeDefined(); + }); + + it("should save to dictionary", () => { + const instance = new AnthropicToolDefinition(); + const data = instance.save(); + expect(data).toBeDefined(); + expect(typeof data).toBe("object"); + }); + }); +}); diff --git a/runtime/typescript/packages/core/tests/model/wire/anthropic-tool-result-block.test.ts b/runtime/typescript/packages/core/tests/model/wire/anthropic-tool-result-block.test.ts new file mode 100644 index 00000000..9d3f0019 --- /dev/null +++ b/runtime/typescript/packages/core/tests/model/wire/anthropic-tool-result-block.test.ts @@ -0,0 +1,71 @@ +// Copyright (c) Microsoft. All rights reserved. +// WARNING: This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. + +import { AnthropicToolResultBlock } from "../../../src/model/index"; + +describe("AnthropicToolResultBlock", () => { + describe("construction", () => { + it("should create a new instance with defaults", () => { + const instance = new AnthropicToolResultBlock(); + expect(instance).toBeDefined(); + }); + + it("should create a new instance with partial initialization", () => { + const instance = new AnthropicToolResultBlock({}); + expect(instance).toBeDefined(); + }); + }); + + describe("JSON serialization", () => { + it("should load from JSON - example 1", () => { + const json = `{\n "tool_use_id": "toolu_01A09q90qw90lq917835lq9",\n "content": "72°F and sunny in Paris"\n}`; + const instance = AnthropicToolResultBlock.fromJson(json); + expect(instance).toBeDefined(); + expect(instance.tool_use_id).toEqual("toolu_01A09q90qw90lq917835lq9"); + expect(instance.content).toEqual("72°F and sunny in Paris"); + }); + + it("should round-trip JSON - example 1", () => { + const json = `{\n "tool_use_id": "toolu_01A09q90qw90lq917835lq9",\n "content": "72°F and sunny in Paris"\n}`; + const instance = AnthropicToolResultBlock.fromJson(json); + const output = instance.toJson(); + const reloaded = AnthropicToolResultBlock.fromJson(output); + expect(reloaded.tool_use_id).toEqual(instance.tool_use_id); + expect(reloaded.content).toEqual(instance.content); + }); + }); + + describe("YAML serialization", () => { + it("should load from YAML - example 1", () => { + const yaml = `tool_use_id: toolu_01A09q90qw90lq917835lq9\ncontent: 72°F and sunny in Paris\n`; + const instance = AnthropicToolResultBlock.fromYaml(yaml); + expect(instance).toBeDefined(); + expect(instance.tool_use_id).toEqual("toolu_01A09q90qw90lq917835lq9"); + expect(instance.content).toEqual("72°F and sunny in Paris"); + }); + + it("should round-trip YAML - example 1", () => { + const yaml = `tool_use_id: toolu_01A09q90qw90lq917835lq9\ncontent: 72°F and sunny in Paris\n`; + const instance = AnthropicToolResultBlock.fromYaml(yaml); + const output = instance.toYaml(); + const reloaded = AnthropicToolResultBlock.fromYaml(output); + expect(reloaded.tool_use_id).toEqual(instance.tool_use_id); + expect(reloaded.content).toEqual(instance.content); + }); + }); + + describe("load and save", () => { + it("should load from dictionary", () => { + const data: Record = {}; + const instance = AnthropicToolResultBlock.load(data); + expect(instance).toBeDefined(); + }); + + it("should save to dictionary", () => { + const instance = new AnthropicToolResultBlock(); + const data = instance.save(); + expect(data).toBeDefined(); + expect(typeof data).toBe("object"); + }); + }); +}); diff --git a/runtime/typescript/packages/core/tests/model/wire/anthropic-tool-use-block.test.ts b/runtime/typescript/packages/core/tests/model/wire/anthropic-tool-use-block.test.ts new file mode 100644 index 00000000..c1afe495 --- /dev/null +++ b/runtime/typescript/packages/core/tests/model/wire/anthropic-tool-use-block.test.ts @@ -0,0 +1,71 @@ +// Copyright (c) Microsoft. All rights reserved. +// WARNING: This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. + +import { AnthropicToolUseBlock } from "../../../src/model/index"; + +describe("AnthropicToolUseBlock", () => { + describe("construction", () => { + it("should create a new instance with defaults", () => { + const instance = new AnthropicToolUseBlock(); + expect(instance).toBeDefined(); + }); + + it("should create a new instance with partial initialization", () => { + const instance = new AnthropicToolUseBlock({}); + expect(instance).toBeDefined(); + }); + }); + + describe("JSON serialization", () => { + it("should load from JSON - example 1", () => { + const json = `{\n "id": "toolu_01A09q90qw90lq917835lq9",\n "name": "get_weather",\n "input": {\n "city": "Paris"\n }\n}`; + const instance = AnthropicToolUseBlock.fromJson(json); + expect(instance).toBeDefined(); + expect(instance.id).toEqual("toolu_01A09q90qw90lq917835lq9"); + expect(instance.name).toEqual("get_weather"); + }); + + it("should round-trip JSON - example 1", () => { + const json = `{\n "id": "toolu_01A09q90qw90lq917835lq9",\n "name": "get_weather",\n "input": {\n "city": "Paris"\n }\n}`; + const instance = AnthropicToolUseBlock.fromJson(json); + const output = instance.toJson(); + const reloaded = AnthropicToolUseBlock.fromJson(output); + expect(reloaded.id).toEqual(instance.id); + expect(reloaded.name).toEqual(instance.name); + }); + }); + + describe("YAML serialization", () => { + it("should load from YAML - example 1", () => { + const yaml = `id: toolu_01A09q90qw90lq917835lq9\nname: get_weather\ninput:\n city: Paris\n`; + const instance = AnthropicToolUseBlock.fromYaml(yaml); + expect(instance).toBeDefined(); + expect(instance.id).toEqual("toolu_01A09q90qw90lq917835lq9"); + expect(instance.name).toEqual("get_weather"); + }); + + it("should round-trip YAML - example 1", () => { + const yaml = `id: toolu_01A09q90qw90lq917835lq9\nname: get_weather\ninput:\n city: Paris\n`; + const instance = AnthropicToolUseBlock.fromYaml(yaml); + const output = instance.toYaml(); + const reloaded = AnthropicToolUseBlock.fromYaml(output); + expect(reloaded.id).toEqual(instance.id); + expect(reloaded.name).toEqual(instance.name); + }); + }); + + describe("load and save", () => { + it("should load from dictionary", () => { + const data: Record = {}; + const instance = AnthropicToolUseBlock.load(data); + expect(instance).toBeDefined(); + }); + + it("should save to dictionary", () => { + const instance = new AnthropicToolUseBlock(); + const data = instance.save(); + expect(data).toBeDefined(); + expect(typeof data).toBe("object"); + }); + }); +}); diff --git a/runtime/typescript/packages/core/tests/model/wire/anthropic-usage.test.ts b/runtime/typescript/packages/core/tests/model/wire/anthropic-usage.test.ts new file mode 100644 index 00000000..1f66ac2f --- /dev/null +++ b/runtime/typescript/packages/core/tests/model/wire/anthropic-usage.test.ts @@ -0,0 +1,71 @@ +// Copyright (c) Microsoft. All rights reserved. +// WARNING: This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. + +import { AnthropicUsage } from "../../../src/model/index"; + +describe("AnthropicUsage", () => { + describe("construction", () => { + it("should create a new instance with defaults", () => { + const instance = new AnthropicUsage(); + expect(instance).toBeDefined(); + }); + + it("should create a new instance with partial initialization", () => { + const instance = new AnthropicUsage({}); + expect(instance).toBeDefined(); + }); + }); + + describe("JSON serialization", () => { + it("should load from JSON - example 1", () => { + const json = `{\n "input_tokens": 150,\n "output_tokens": 42\n}`; + const instance = AnthropicUsage.fromJson(json); + expect(instance).toBeDefined(); + expect(instance.input_tokens).toEqual(150); + expect(instance.output_tokens).toEqual(42); + }); + + it("should round-trip JSON - example 1", () => { + const json = `{\n "input_tokens": 150,\n "output_tokens": 42\n}`; + const instance = AnthropicUsage.fromJson(json); + const output = instance.toJson(); + const reloaded = AnthropicUsage.fromJson(output); + expect(reloaded.input_tokens).toEqual(instance.input_tokens); + expect(reloaded.output_tokens).toEqual(instance.output_tokens); + }); + }); + + describe("YAML serialization", () => { + it("should load from YAML - example 1", () => { + const yaml = `input_tokens: 150\noutput_tokens: 42\n`; + const instance = AnthropicUsage.fromYaml(yaml); + expect(instance).toBeDefined(); + expect(instance.input_tokens).toEqual(150); + expect(instance.output_tokens).toEqual(42); + }); + + it("should round-trip YAML - example 1", () => { + const yaml = `input_tokens: 150\noutput_tokens: 42\n`; + const instance = AnthropicUsage.fromYaml(yaml); + const output = instance.toYaml(); + const reloaded = AnthropicUsage.fromYaml(output); + expect(reloaded.input_tokens).toEqual(instance.input_tokens); + expect(reloaded.output_tokens).toEqual(instance.output_tokens); + }); + }); + + describe("load and save", () => { + it("should load from dictionary", () => { + const data: Record = {}; + const instance = AnthropicUsage.load(data); + expect(instance).toBeDefined(); + }); + + it("should save to dictionary", () => { + const instance = new AnthropicUsage(); + const data = instance.save(); + expect(data).toBeDefined(); + expect(typeof data).toBe("object"); + }); + }); +}); diff --git a/runtime/typescript/packages/core/tests/model/wire/anthropic-wire-message.test.ts b/runtime/typescript/packages/core/tests/model/wire/anthropic-wire-message.test.ts new file mode 100644 index 00000000..8b6afac9 --- /dev/null +++ b/runtime/typescript/packages/core/tests/model/wire/anthropic-wire-message.test.ts @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft. All rights reserved. +// WARNING: This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. + +import { AnthropicWireMessage } from "../../../src/model/index"; + +describe("AnthropicWireMessage", () => { + describe("construction", () => { + it("should create a new instance with defaults", () => { + const instance = new AnthropicWireMessage(); + expect(instance).toBeDefined(); + }); + + it("should create a new instance with partial initialization", () => { + const instance = new AnthropicWireMessage({}); + expect(instance).toBeDefined(); + }); + }); + + describe("JSON serialization", () => { + it("should load from JSON - example 1", () => { + const json = `{\n "role": "user"\n}`; + const instance = AnthropicWireMessage.fromJson(json); + expect(instance).toBeDefined(); + expect(instance.role).toEqual("user"); + }); + + it("should round-trip JSON - example 1", () => { + const json = `{\n "role": "user"\n}`; + const instance = AnthropicWireMessage.fromJson(json); + const output = instance.toJson(); + const reloaded = AnthropicWireMessage.fromJson(output); + expect(reloaded.role).toEqual(instance.role); + }); + }); + + describe("YAML serialization", () => { + it("should load from YAML - example 1", () => { + const yaml = `role: user\n`; + const instance = AnthropicWireMessage.fromYaml(yaml); + expect(instance).toBeDefined(); + expect(instance.role).toEqual("user"); + }); + + it("should round-trip YAML - example 1", () => { + const yaml = `role: user\n`; + const instance = AnthropicWireMessage.fromYaml(yaml); + const output = instance.toYaml(); + const reloaded = AnthropicWireMessage.fromYaml(output); + expect(reloaded.role).toEqual(instance.role); + }); + }); + + describe("load and save", () => { + it("should load from dictionary", () => { + const data: Record = {}; + const instance = AnthropicWireMessage.load(data); + expect(instance).toBeDefined(); + }); + + it("should save to dictionary", () => { + const instance = new AnthropicWireMessage(); + const data = instance.save(); + expect(data).toBeDefined(); + expect(typeof data).toBe("object"); + }); + }); +}); diff --git a/schema/model/core/errors.tsp b/schema/model/core/errors.tsp new file mode 100644 index 00000000..9d8a552a --- /dev/null +++ b/schema/model/core/errors.tsp @@ -0,0 +1,54 @@ +import "prompty-emitter"; + +namespace Prompty; + +/** + * Raised when no invoker implementation is registered for a given component + * and key. For example, if no renderer is registered for the key "jinja2", + * an InvokerError is raised. + */ +model InvokerError { + @doc("Human-readable error message") + @sample(#{ message: "No renderer registered for key: jinja2" }) + message: string; + + @doc("The pipeline component type that was being looked up (e.g., 'renderer', 'parser', 'executor', 'processor')") + @sample(#{ component: "renderer" }) + component: string; + + @doc("The registration key that was not found") + @sample(#{ key: "jinja2" }) + key: string; +} + +/** + * Raised when input validation fails. Each ValidationError describes a + * single property that did not satisfy its constraint. + */ +model ValidationError { + @doc("Human-readable error message") + @sample(#{ message: "Missing required input: firstName" }) + message: string; + + @doc("The name of the property that failed validation") + @sample(#{ property: "firstName" }) + property: string; + + @doc("The constraint that was violated (e.g., 'required', 'type')") + @sample(#{ constraint: "required" }) + constraint: string; +} + +/** + * Raised when a referenced file cannot be found. This applies to both + * .prompty files and ${file:path} references in frontmatter. + */ +model FileNotFoundError { + @doc("Human-readable error message") + @sample(#{ message: "Prompty file not found: ./chat.prompty" }) + message: string; + + @doc("The file path that could not be resolved") + @sample(#{ path: "./chat.prompty" }) + path: string; +} diff --git a/schema/model/core/validation.tsp b/schema/model/core/validation.tsp new file mode 100644 index 00000000..fa52f577 --- /dev/null +++ b/schema/model/core/validation.tsp @@ -0,0 +1,19 @@ +import "prompty-emitter"; +import "./errors.tsp"; + +namespace Prompty; + +/** + * The result of validating inputs against an agent's inputSchema. + * Returned by `validate_inputs` (§12.2) to indicate whether all + * required inputs are present and satisfy their constraints. + */ +model ValidationResult { + @doc("Whether all inputs passed validation") + @sample(#{ valid: true }) + valid: boolean; + + @doc("List of validation errors (empty when valid is true)") + @sample(#{ errors: #[] }) + errors: ValidationError[]; +} diff --git a/schema/model/main.tsp b/schema/model/main.tsp index 5bde9d80..a10c6ec5 100644 --- a/schema/model/main.tsp +++ b/schema/model/main.tsp @@ -10,6 +10,10 @@ import "./conversation/content.tsp"; import "./conversation/tool-invocation.tsp"; import "./conversation/thread.tsp"; +// Core (properties, errors, validation) +import "./core/errors.tsp"; +import "./core/validation.tsp"; + // Model + usage + discovery import "./model/usage.tsp"; import "./model/discovery.tsp"; @@ -25,6 +29,12 @@ import "./pipeline/processor.tsp"; import "./events/payloads.tsp"; import "./events/stream-chunks.tsp"; +// Streaming +import "./streaming/stream.tsp"; + +// Tracing +import "./tracing/tracer.tsp"; + // Tools (declarations + dispatch) import "./tools/tool.tsp"; import "./tools/mcp.tsp"; @@ -40,3 +50,4 @@ import "./connection/foundry.tsp"; // Wire format mappings (augment existing models with provider field names) import "./wire/openai.tsp"; import "./wire/anthropic.tsp"; +import "./wire/anthropic-types.tsp"; diff --git a/schema/model/streaming/stream.tsp b/schema/model/streaming/stream.tsp new file mode 100644 index 00000000..7e1222a1 --- /dev/null +++ b/schema/model/streaming/stream.tsp @@ -0,0 +1,13 @@ +import "prompty-emitter"; + +namespace Prompty; + +/** + * Options controlling streaming behavior for LLM API calls. + * Passed alongside the model options when streaming is enabled. + */ +model StreamOptions { + @doc("When true, the final streaming chunk includes token usage statistics") + @sample(#{ includeUsage: true }) + includeUsage?: boolean; +} diff --git a/schema/model/tracing/tracer.tsp b/schema/model/tracing/tracer.tsp new file mode 100644 index 00000000..a1599592 --- /dev/null +++ b/schema/model/tracing/tracer.tsp @@ -0,0 +1,107 @@ +import "prompty-emitter"; +import "../model/usage.tsp"; + +namespace Prompty; + +/** + * A recursive union of JSON-safe primitive types used for trace values. + * All values emitted to tracing backends are serialized to TraceValue + * via the `to_dict` algorithm (§3.3). + * + * Note: The recursive nature (arrays and records of TraceValue) is + * expressed as `unknown` to avoid infinite recursion in code generators. + * Implementations should treat array/record variants as containing + * nested TraceValue instances. + */ +union TraceValue { + @doc("A string trace value") + string: string, + + @doc("An integer trace value") + integer: int32, + + @doc("A floating-point trace value") + float: float64, + + @doc("A boolean trace value") + boolean: boolean, + + @doc("A null trace value") + null: null, + + @doc("An array of trace values (recursive)") + array: unknown[], + + @doc("A record/map of trace values (recursive)") + record: Record, +} + +/** + * Timing information for a trace span. + */ +model TraceTime { + @doc("ISO 8601 UTC timestamp when the span started") + @sample(#{ start: "2026-04-04T12:00:00Z" }) + start: string; + + @doc("ISO 8601 UTC timestamp when the span ended") + @sample(#{ end: "2026-04-04T12:00:01Z" }) + end: string; + + @doc("Duration of the span in milliseconds") + @sample(#{ duration: 1000 }) + duration: float64; +} + +/** + * A single trace span capturing one pipeline stage or function invocation. + * Spans nest via the `__frames` field to form a tree representing the + * full execution (§3.6.1). + */ +model TraceSpan { + @doc("The name of this span (typically the function signature)") + @sample(#{ name: "prompty.core.pipeline.run" }) + name: string; + + @doc("Timing information for this span") + __time: TraceTime; + + @doc("Fully-qualified function signature that produced this span") + @sample(#{ signature: "prompty.core.pipeline.run" }) + signature?: string; + + @doc("Serialized input parameters (redacted per §3.4)") + inputs?: Record; + + @doc("Serialized return value or error information (redacted per §3.4)") + output?: unknown; + + @doc("Error message if the span ended with an exception") + @sample(#{ error: "Connection refused" }) + error?: string; + + @doc("Aggregated token usage hoisted from child spans (§3.5)") + __usage?: TokenUsage; + + @doc("Additional span attributes (e.g., OpenTelemetry GenAI attributes)") + attributes?: Record; + + @doc("Nested child spans forming the execution tree (recursive; each element is a TraceSpan)") + __frames?: unknown[]; +} + +/** + * The top-level .tracy file structure written by the file backend (§3.6.1). + */ +model TraceFile { + @doc("Language/runtime name (e.g., 'python', 'csharp', 'javascript')") + @sample(#{ runtime: "python" }) + runtime: string; + + @doc("Prompty library version") + @sample(#{ version: "2.0.0" }) + version: string; + + @doc("The root trace span") + trace: TraceSpan; +} diff --git a/schema/model/wire/anthropic-types.tsp b/schema/model/wire/anthropic-types.tsp new file mode 100644 index 00000000..43f09c96 --- /dev/null +++ b/schema/model/wire/anthropic-types.tsp @@ -0,0 +1,203 @@ +import "prompty-emitter"; + +namespace Prompty; + +/** + * A text content block in Anthropic's array-of-blocks message format. + */ +model AnthropicTextBlock { + @doc("The content block type") + type: "text"; + + @doc("The text content") + @sample(#{ text: "Hello, how can I help?" }) + text: string; +} + +/** + * An image content block using base64-encoded data. + * Anthropic requires images as base64 with an explicit media type. + */ +model AnthropicImageBlock { + @doc("The content block type") + type: "image"; + + @doc("The image source (base64-encoded)") + source: AnthropicImageSource; +} + +/** + * Source descriptor for an Anthropic base64 image. + */ +model AnthropicImageSource { + @doc("The encoding type (always 'base64' for inline images)") + type: "base64"; + + @doc("The MIME type of the image (e.g., 'image/png', 'image/jpeg')") + @sample(#{ media_type: "image/png" }) + media_type: string; + + @doc("The base64-encoded image data") + @sample(#{ data: "iVBORw0KGgo..." }) + data: string; +} + +/** + * A tool use content block returned in an assistant message when + * the model wants to invoke a tool. + */ +model AnthropicToolUseBlock { + @doc("The content block type") + type: "tool_use"; + + @doc("Unique identifier for this tool invocation") + @sample(#{ id: "toolu_01A09q90qw90lq917835lq9" }) + id: string; + + @doc("The name of the tool to invoke") + @sample(#{ name: "get_weather" }) + name: string; + + @doc("The JSON arguments for the tool call") + @sample(#{ input: #{ city: "Paris" } }) + input: Record; +} + +/** + * A tool result content block sent back to the API with the tool's output. + */ +model AnthropicToolResultBlock { + @doc("The content block type") + type: "tool_result"; + + @doc("The tool_use id this result corresponds to") + @sample(#{ tool_use_id: "toolu_01A09q90qw90lq917835lq9" }) + tool_use_id: string; + + @doc("The tool's output content") + @sample(#{ content: "72°F and sunny in Paris" }) + content: string; +} + +/** + * Union of all Anthropic content block types. + */ +union AnthropicContentBlock { + text: AnthropicTextBlock, + image: AnthropicImageBlock, + toolUse: AnthropicToolUseBlock, + toolResult: AnthropicToolResultBlock, +} + +/** + * A single message in the Anthropic Messages API wire format. + * Anthropic always uses the array-of-blocks form for content, + * even when there is only one text block (§7.5). + */ +model AnthropicWireMessage { + @doc("The message role ('user' or 'assistant')") + @sample(#{ role: "user" }) + role: string; + + @doc("Array of typed content blocks (AnthropicTextBlock | AnthropicImageBlock | AnthropicToolUseBlock | AnthropicToolResultBlock)") + content: unknown[]; +} + +/** + * A tool definition in Anthropic's format. Unlike OpenAI which wraps + * tools in `{type: "function", function: {...}}`, Anthropic uses a + * flat structure with `input_schema` (§7.5). + */ +model AnthropicToolDefinition { + @doc("The tool name") + @sample(#{ name: "get_weather" }) + name: string; + + @doc("A description of what the tool does") + @sample(#{ description: "Get the current weather for a city" }) + description?: string; + + @doc("JSON Schema describing the tool's input parameters") + input_schema: Record; +} + +/** + * The full request body for the Anthropic Messages API (§7.5). + */ +model AnthropicMessagesRequest { + @doc("The model identifier") + @sample(#{ `model`: "claude-sonnet-4-20250514" }) + `model`: string; + + @doc("The non-system messages to send") + messages: AnthropicWireMessage[]; + + @doc("Maximum number of tokens to generate (required by Anthropic)") + @sample(#{ max_tokens: 4096 }) + max_tokens: int32; + + @doc("System prompt text (extracted from system-role messages)") + @sample(#{ system: "You are a helpful assistant." }) + system?: string; + + @doc("Sampling temperature") + @sample(#{ temperature: 0.7 }) + temperature?: float32; + + @doc("Top-P sampling value") + @sample(#{ top_p: 0.9 }) + top_p?: float32; + + @doc("Top-K sampling value") + @sample(#{ top_k: 40 }) + top_k?: int32; + + @doc("Stop sequences to end generation") + @sample(#{ stop_sequences: #["\n\nHuman:"] }) + stop_sequences?: string[]; + + @doc("Tool definitions available to the model") + tools?: AnthropicToolDefinition[]; +} + +/** + * Usage statistics returned in an Anthropic Messages API response. + */ +model AnthropicUsage { + @doc("Number of input tokens consumed") + @sample(#{ input_tokens: 150 }) + input_tokens: int32; + + @doc("Number of output tokens generated") + @sample(#{ output_tokens: 42 }) + output_tokens: int32; +} + +/** + * The response body from the Anthropic Messages API. + */ +model AnthropicMessagesResponse { + @doc("Unique response identifier") + @sample(#{ id: "msg_01XFDUDYJgAACzvnptvVoYEL" }) + id: string; + + @doc("Object type (always 'message')") + type: "message"; + + @doc("The role of the response (always 'assistant')") + role: "assistant"; + + @doc("Array of content blocks in the response (AnthropicTextBlock | AnthropicToolUseBlock)") + content: unknown[]; + + @doc("The model that generated the response") + @sample(#{ `model`: "claude-sonnet-4-20250514" }) + `model`: string; + + @doc("The reason generation stopped ('end_turn', 'max_tokens', 'stop_sequence', 'tool_use')") + @sample(#{ stop_reason: "end_turn" }) + stop_reason: string; + + @doc("Token usage statistics") + usage: AnthropicUsage; +} diff --git a/vscode/prompty/schemas/AnthropicContentBlock.yaml b/vscode/prompty/schemas/AnthropicContentBlock.yaml new file mode 100644 index 00000000..2b2a98b6 --- /dev/null +++ b/vscode/prompty/schemas/AnthropicContentBlock.yaml @@ -0,0 +1,8 @@ +$schema: https://json-schema.org/draft/2020-12/schema +$id: AnthropicContentBlock.yaml +anyOf: + - $ref: AnthropicTextBlock.yaml + - $ref: AnthropicImageBlock.yaml + - $ref: AnthropicToolUseBlock.yaml + - $ref: AnthropicToolResultBlock.yaml +description: Union of all Anthropic content block types. diff --git a/vscode/prompty/schemas/AnthropicImageBlock.yaml b/vscode/prompty/schemas/AnthropicImageBlock.yaml new file mode 100644 index 00000000..b959743d --- /dev/null +++ b/vscode/prompty/schemas/AnthropicImageBlock.yaml @@ -0,0 +1,17 @@ +$schema: https://json-schema.org/draft/2020-12/schema +$id: AnthropicImageBlock.yaml +type: object +properties: + type: + type: string + const: image + description: The content block type + source: + $ref: AnthropicImageSource.yaml + description: The image source (base64-encoded) +required: + - type + - source +description: |- + An image content block using base64-encoded data. + Anthropic requires images as base64 with an explicit media type. diff --git a/vscode/prompty/schemas/AnthropicImageSource.yaml b/vscode/prompty/schemas/AnthropicImageSource.yaml new file mode 100644 index 00000000..dc5820a5 --- /dev/null +++ b/vscode/prompty/schemas/AnthropicImageSource.yaml @@ -0,0 +1,19 @@ +$schema: https://json-schema.org/draft/2020-12/schema +$id: AnthropicImageSource.yaml +type: object +properties: + type: + type: string + const: base64 + description: The encoding type (always 'base64' for inline images) + media_type: + type: string + description: The MIME type of the image (e.g., 'image/png', 'image/jpeg') + data: + type: string + description: The base64-encoded image data +required: + - type + - media_type + - data +description: Source descriptor for an Anthropic base64 image. diff --git a/vscode/prompty/schemas/AnthropicMessagesRequest.yaml b/vscode/prompty/schemas/AnthropicMessagesRequest.yaml new file mode 100644 index 00000000..1d177c13 --- /dev/null +++ b/vscode/prompty/schemas/AnthropicMessagesRequest.yaml @@ -0,0 +1,46 @@ +$schema: https://json-schema.org/draft/2020-12/schema +$id: AnthropicMessagesRequest.yaml +type: object +properties: + model: + type: string + description: The model identifier + messages: + type: array + items: + $ref: AnthropicWireMessage.yaml + description: The non-system messages to send + max_tokens: + type: integer + minimum: -2147483648 + maximum: 2147483647 + description: Maximum number of tokens to generate (required by Anthropic) + system: + type: string + description: System prompt text (extracted from system-role messages) + temperature: + type: number + description: Sampling temperature + top_p: + type: number + description: Top-P sampling value + top_k: + type: integer + minimum: -2147483648 + maximum: 2147483647 + description: Top-K sampling value + stop_sequences: + type: array + items: + type: string + description: Stop sequences to end generation + tools: + type: array + items: + $ref: AnthropicToolDefinition.yaml + description: Tool definitions available to the model +required: + - model + - messages + - max_tokens +description: The full request body for the Anthropic Messages API (§7.5). diff --git a/vscode/prompty/schemas/AnthropicMessagesResponse.yaml b/vscode/prompty/schemas/AnthropicMessagesResponse.yaml new file mode 100644 index 00000000..61db7b86 --- /dev/null +++ b/vscode/prompty/schemas/AnthropicMessagesResponse.yaml @@ -0,0 +1,37 @@ +$schema: https://json-schema.org/draft/2020-12/schema +$id: AnthropicMessagesResponse.yaml +type: object +properties: + id: + type: string + description: Unique response identifier + type: + type: string + const: message + description: Object type (always 'message') + role: + type: string + const: assistant + description: The role of the response (always 'assistant') + content: + type: array + items: {} + description: Array of content blocks in the response (AnthropicTextBlock | AnthropicToolUseBlock) + model: + type: string + description: The model that generated the response + stop_reason: + type: string + description: The reason generation stopped ('end_turn', 'max_tokens', 'stop_sequence', 'tool_use') + usage: + $ref: AnthropicUsage.yaml + description: Token usage statistics +required: + - id + - type + - role + - content + - model + - stop_reason + - usage +description: The response body from the Anthropic Messages API. diff --git a/vscode/prompty/schemas/AnthropicTextBlock.yaml b/vscode/prompty/schemas/AnthropicTextBlock.yaml new file mode 100644 index 00000000..8585674b --- /dev/null +++ b/vscode/prompty/schemas/AnthropicTextBlock.yaml @@ -0,0 +1,15 @@ +$schema: https://json-schema.org/draft/2020-12/schema +$id: AnthropicTextBlock.yaml +type: object +properties: + type: + type: string + const: text + description: The content block type + text: + type: string + description: The text content +required: + - type + - text +description: A text content block in Anthropic's array-of-blocks message format. diff --git a/vscode/prompty/schemas/AnthropicToolDefinition.yaml b/vscode/prompty/schemas/AnthropicToolDefinition.yaml new file mode 100644 index 00000000..3b4e3810 --- /dev/null +++ b/vscode/prompty/schemas/AnthropicToolDefinition.yaml @@ -0,0 +1,20 @@ +$schema: https://json-schema.org/draft/2020-12/schema +$id: AnthropicToolDefinition.yaml +type: object +properties: + name: + type: string + description: The tool name + description: + type: string + description: A description of what the tool does + input_schema: + $ref: RecordUnknown.yaml + description: JSON Schema describing the tool's input parameters +required: + - name + - input_schema +description: |- + A tool definition in Anthropic's format. Unlike OpenAI which wraps + tools in `{type: "function", function: {...}}`, Anthropic uses a + flat structure with `input_schema` (§7.5). diff --git a/vscode/prompty/schemas/AnthropicToolResultBlock.yaml b/vscode/prompty/schemas/AnthropicToolResultBlock.yaml new file mode 100644 index 00000000..3494d694 --- /dev/null +++ b/vscode/prompty/schemas/AnthropicToolResultBlock.yaml @@ -0,0 +1,19 @@ +$schema: https://json-schema.org/draft/2020-12/schema +$id: AnthropicToolResultBlock.yaml +type: object +properties: + type: + type: string + const: tool_result + description: The content block type + tool_use_id: + type: string + description: The tool_use id this result corresponds to + content: + type: string + description: The tool's output content +required: + - type + - tool_use_id + - content +description: A tool result content block sent back to the API with the tool's output. diff --git a/vscode/prompty/schemas/AnthropicToolUseBlock.yaml b/vscode/prompty/schemas/AnthropicToolUseBlock.yaml new file mode 100644 index 00000000..d898ea66 --- /dev/null +++ b/vscode/prompty/schemas/AnthropicToolUseBlock.yaml @@ -0,0 +1,25 @@ +$schema: https://json-schema.org/draft/2020-12/schema +$id: AnthropicToolUseBlock.yaml +type: object +properties: + type: + type: string + const: tool_use + description: The content block type + id: + type: string + description: Unique identifier for this tool invocation + name: + type: string + description: The name of the tool to invoke + input: + $ref: RecordUnknown.yaml + description: The JSON arguments for the tool call +required: + - type + - id + - name + - input +description: |- + A tool use content block returned in an assistant message when + the model wants to invoke a tool. diff --git a/vscode/prompty/schemas/AnthropicUsage.yaml b/vscode/prompty/schemas/AnthropicUsage.yaml new file mode 100644 index 00000000..58a21b3f --- /dev/null +++ b/vscode/prompty/schemas/AnthropicUsage.yaml @@ -0,0 +1,18 @@ +$schema: https://json-schema.org/draft/2020-12/schema +$id: AnthropicUsage.yaml +type: object +properties: + input_tokens: + type: integer + minimum: -2147483648 + maximum: 2147483647 + description: Number of input tokens consumed + output_tokens: + type: integer + minimum: -2147483648 + maximum: 2147483647 + description: Number of output tokens generated +required: + - input_tokens + - output_tokens +description: Usage statistics returned in an Anthropic Messages API response. diff --git a/vscode/prompty/schemas/AnthropicWireMessage.yaml b/vscode/prompty/schemas/AnthropicWireMessage.yaml new file mode 100644 index 00000000..6b3736a1 --- /dev/null +++ b/vscode/prompty/schemas/AnthropicWireMessage.yaml @@ -0,0 +1,18 @@ +$schema: https://json-schema.org/draft/2020-12/schema +$id: AnthropicWireMessage.yaml +type: object +properties: + role: + type: string + description: The message role ('user' or 'assistant') + content: + type: array + items: {} + description: Array of typed content blocks (AnthropicTextBlock | AnthropicImageBlock | AnthropicToolUseBlock | AnthropicToolResultBlock) +required: + - role + - content +description: |- + A single message in the Anthropic Messages API wire format. + Anthropic always uses the array-of-blocks form for content, + even when there is only one text block (§7.5). diff --git a/vscode/prompty/schemas/FileNotFoundError.yaml b/vscode/prompty/schemas/FileNotFoundError.yaml new file mode 100644 index 00000000..0c194e5e --- /dev/null +++ b/vscode/prompty/schemas/FileNotFoundError.yaml @@ -0,0 +1,16 @@ +$schema: https://json-schema.org/draft/2020-12/schema +$id: FileNotFoundError.yaml +type: object +properties: + message: + type: string + description: Human-readable error message + path: + type: string + description: The file path that could not be resolved +required: + - message + - path +description: |- + Raised when a referenced file cannot be found. This applies to both + .prompty files and ${file:path} references in frontmatter. diff --git a/vscode/prompty/schemas/InvokerError.yaml b/vscode/prompty/schemas/InvokerError.yaml new file mode 100644 index 00000000..8f1f0e51 --- /dev/null +++ b/vscode/prompty/schemas/InvokerError.yaml @@ -0,0 +1,21 @@ +$schema: https://json-schema.org/draft/2020-12/schema +$id: InvokerError.yaml +type: object +properties: + message: + type: string + description: Human-readable error message + component: + type: string + description: The pipeline component type that was being looked up (e.g., 'renderer', 'parser', 'executor', 'processor') + key: + type: string + description: The registration key that was not found +required: + - message + - component + - key +description: |- + Raised when no invoker implementation is registered for a given component + and key. For example, if no renderer is registered for the key "jinja2", + an InvokerError is raised. diff --git a/vscode/prompty/schemas/RecordTraceValue.yaml b/vscode/prompty/schemas/RecordTraceValue.yaml new file mode 100644 index 00000000..8ed763fd --- /dev/null +++ b/vscode/prompty/schemas/RecordTraceValue.yaml @@ -0,0 +1,6 @@ +$schema: https://json-schema.org/draft/2020-12/schema +$id: RecordTraceValue.yaml +type: object +properties: {} +unevaluatedProperties: + $ref: TraceValue.yaml diff --git a/vscode/prompty/schemas/StreamOptions.yaml b/vscode/prompty/schemas/StreamOptions.yaml new file mode 100644 index 00000000..79cd60c2 --- /dev/null +++ b/vscode/prompty/schemas/StreamOptions.yaml @@ -0,0 +1,10 @@ +$schema: https://json-schema.org/draft/2020-12/schema +$id: StreamOptions.yaml +type: object +properties: + includeUsage: + type: boolean + description: When true, the final streaming chunk includes token usage statistics +description: |- + Options controlling streaming behavior for LLM API calls. + Passed alongside the model options when streaming is enabled. diff --git a/vscode/prompty/schemas/TraceFile.yaml b/vscode/prompty/schemas/TraceFile.yaml new file mode 100644 index 00000000..92963a89 --- /dev/null +++ b/vscode/prompty/schemas/TraceFile.yaml @@ -0,0 +1,18 @@ +$schema: https://json-schema.org/draft/2020-12/schema +$id: TraceFile.yaml +type: object +properties: + runtime: + type: string + description: Language/runtime name (e.g., 'python', 'csharp', 'javascript') + version: + type: string + description: Prompty library version + trace: + $ref: TraceSpan.yaml + description: The root trace span +required: + - runtime + - version + - trace +description: The top-level .tracy file structure written by the file backend (§3.6.1). diff --git a/vscode/prompty/schemas/TraceSpan.yaml b/vscode/prompty/schemas/TraceSpan.yaml new file mode 100644 index 00000000..526e54ae --- /dev/null +++ b/vscode/prompty/schemas/TraceSpan.yaml @@ -0,0 +1,38 @@ +$schema: https://json-schema.org/draft/2020-12/schema +$id: TraceSpan.yaml +type: object +properties: + name: + type: string + description: The name of this span (typically the function signature) + __time: + $ref: TraceTime.yaml + description: Timing information for this span + signature: + type: string + description: Fully-qualified function signature that produced this span + inputs: + $ref: RecordUnknown.yaml + description: Serialized input parameters (redacted per §3.4) + output: + description: Serialized return value or error information (redacted per §3.4) + error: + type: string + description: Error message if the span ended with an exception + __usage: + $ref: TokenUsage.yaml + description: Aggregated token usage hoisted from child spans (§3.5) + attributes: + $ref: RecordUnknown.yaml + description: Additional span attributes (e.g., OpenTelemetry GenAI attributes) + __frames: + type: array + items: {} + description: Nested child spans forming the execution tree (recursive; each element is a TraceSpan) +required: + - name + - __time +description: |- + A single trace span capturing one pipeline stage or function invocation. + Spans nest via the `__frames` field to form a tree representing the + full execution (§3.6.1). diff --git a/vscode/prompty/schemas/TraceTime.yaml b/vscode/prompty/schemas/TraceTime.yaml new file mode 100644 index 00000000..526d88cd --- /dev/null +++ b/vscode/prompty/schemas/TraceTime.yaml @@ -0,0 +1,18 @@ +$schema: https://json-schema.org/draft/2020-12/schema +$id: TraceTime.yaml +type: object +properties: + start: + type: string + description: ISO 8601 UTC timestamp when the span started + end: + type: string + description: ISO 8601 UTC timestamp when the span ended + duration: + type: number + description: Duration of the span in milliseconds +required: + - start + - end + - duration +description: Timing information for a trace span. diff --git a/vscode/prompty/schemas/TraceValue.yaml b/vscode/prompty/schemas/TraceValue.yaml new file mode 100644 index 00000000..5b3aed3d --- /dev/null +++ b/vscode/prompty/schemas/TraceValue.yaml @@ -0,0 +1,29 @@ +$schema: https://json-schema.org/draft/2020-12/schema +$id: TraceValue.yaml +anyOf: + - type: string + description: A string trace value + - type: integer + minimum: -2147483648 + maximum: 2147483647 + description: An integer trace value + - type: number + description: A floating-point trace value + - type: boolean + description: A boolean trace value + - type: "null" + description: A null trace value + - type: array + items: {} + description: An array of trace values (recursive) + - $ref: RecordUnknown.yaml + description: A record/map of trace values (recursive) +description: |- + A recursive union of JSON-safe primitive types used for trace values. + All values emitted to tracing backends are serialized to TraceValue + via the `to_dict` algorithm (§3.3). + + Note: The recursive nature (arrays and records of TraceValue) is + expressed as `unknown` to avoid infinite recursion in code generators. + Implementations should treat array/record variants as containing + nested TraceValue instances. diff --git a/vscode/prompty/schemas/ValidationError.yaml b/vscode/prompty/schemas/ValidationError.yaml new file mode 100644 index 00000000..51e86977 --- /dev/null +++ b/vscode/prompty/schemas/ValidationError.yaml @@ -0,0 +1,20 @@ +$schema: https://json-schema.org/draft/2020-12/schema +$id: ValidationError.yaml +type: object +properties: + message: + type: string + description: Human-readable error message + property: + type: string + description: The name of the property that failed validation + constraint: + type: string + description: The constraint that was violated (e.g., 'required', 'type') +required: + - message + - property + - constraint +description: |- + Raised when input validation fails. Each ValidationError describes a + single property that did not satisfy its constraint. diff --git a/vscode/prompty/schemas/ValidationResult.yaml b/vscode/prompty/schemas/ValidationResult.yaml new file mode 100644 index 00000000..a5158081 --- /dev/null +++ b/vscode/prompty/schemas/ValidationResult.yaml @@ -0,0 +1,19 @@ +$schema: https://json-schema.org/draft/2020-12/schema +$id: ValidationResult.yaml +type: object +properties: + valid: + type: boolean + description: Whether all inputs passed validation + errors: + type: array + items: + $ref: ValidationError.yaml + description: List of validation errors (empty when valid is true) +required: + - valid + - errors +description: |- + The result of validating inputs against an agent's inputSchema. + Returned by `validate_inputs` (§12.2) to indicate whether all + required inputs are present and satisfy their constraints. diff --git a/web/docs-examples/csharp/Examples/ChatPipeline.cs b/web/docs-examples/csharp/Examples/ChatPipeline.cs index da61a8d8..2fff2d1e 100644 --- a/web/docs-examples/csharp/Examples/ChatPipeline.cs +++ b/web/docs-examples/csharp/Examples/ChatPipeline.cs @@ -46,7 +46,7 @@ public static async Task> PrepareOnlyAsync(string promptyPath, Dic var rendered = await Pipeline.RenderAsync(agent, validatedInputs); // Parse rendered text into messages - var messages = await Pipeline.ParseAsync(agent, rendered); + var messages = await Pipeline.ParseAsync(agent, rendered, null); return messages; } diff --git a/web/docs-examples/csharp/Tests/ChatTests.cs b/web/docs-examples/csharp/Tests/ChatTests.cs index 9d8a556e..c2ad2bfc 100644 --- a/web/docs-examples/csharp/Tests/ChatTests.cs +++ b/web/docs-examples/csharp/Tests/ChatTests.cs @@ -11,6 +11,7 @@ namespace DocsExamples.Tests; /// Tests for the ChatBasic example, verifying the pipeline works correctly /// with mocked executor and processor. /// +[Collection("DocsExamples")] public class ChatTests : IDisposable { /// @@ -59,11 +60,11 @@ public async Task ChatBasic_LoadsAndPrepares() Assert.True(messages.Count >= 2, "Should have at least a system and user message"); // Verify system message - var systemMsg = messages.First(m => m.Role == Roles.System); + var systemMsg = messages.First(m => m.Role == Role.System); Assert.Contains("helpful assistant", systemMsg.Text); // Verify user message - var userMsg = messages.First(m => m.Role == Roles.User); + var userMsg = messages.First(m => m.Role == Role.User); Assert.Contains("What is Prompty?", userMsg.Text); } @@ -78,7 +79,7 @@ public async Task ChatBasic_UsesDefaultInputs_WhenNoneProvided() // Pass no inputs — should use defaults from the prompty file var messages = await Pipeline.PrepareAsync(agent); - var userMsg = messages.First(m => m.Role == Roles.User); + var userMsg = messages.First(m => m.Role == Role.User); // The default question in chat-basic.prompty is "What is Prompty?" Assert.Contains("What is Prompty?", userMsg.Text); } @@ -136,7 +137,7 @@ public async Task ChatPipeline_StepByStep_ProducesMessages() Assert.Contains("system:", rendered); // Step 3: Parse - var messages = await Pipeline.ParseAsync(agent, rendered); + var messages = await Pipeline.ParseAsync(agent, rendered, null); Assert.True(messages.Count >= 2); } diff --git a/web/docs-examples/csharp/Tests/PromptyLoadsTests.cs b/web/docs-examples/csharp/Tests/PromptyLoadsTests.cs index aa5f2656..6006ec52 100644 --- a/web/docs-examples/csharp/Tests/PromptyLoadsTests.cs +++ b/web/docs-examples/csharp/Tests/PromptyLoadsTests.cs @@ -9,6 +9,7 @@ namespace DocsExamples.Tests; /// Parametric tests that load every .prompty file from the shared prompts directory /// and validate they load without error. /// +[Collection("DocsExamples")] public class PromptyLoadsTests { /// diff --git a/web/docs-examples/csharp/Tests/TestCollections.cs b/web/docs-examples/csharp/Tests/TestCollections.cs new file mode 100644 index 00000000..9e82c9ae --- /dev/null +++ b/web/docs-examples/csharp/Tests/TestCollections.cs @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft. All rights reserved. + +using Xunit; + +namespace DocsExamples.Tests; + +/// +/// Serializes all doc-example tests so env-var mutations don't race between classes. +/// +[CollectionDefinition("DocsExamples")] +public class DocsExamplesCollection : ICollectionFixture; diff --git a/web/src/content/docs/reference/AnthropicImageBlock.md b/web/src/content/docs/reference/AnthropicImageBlock.md new file mode 100644 index 00000000..91eec89f --- /dev/null +++ b/web/src/content/docs/reference/AnthropicImageBlock.md @@ -0,0 +1,45 @@ +--- +title: "AnthropicImageBlock" +description: "Documentation for the AnthropicImageBlock type." +slug: "reference/anthropicimageblock" +--- + +An image content block using base64-encoded data. +Anthropic requires images as base64 with an explicit media type. + +## Class Diagram + +```mermaid +--- +title: AnthropicImageBlock +config: + look: handDrawn + theme: colorful + class: + hideEmptyMembersBox: true +--- +classDiagram + class AnthropicImageBlock { + +string type + +AnthropicImageSource source + } + class AnthropicImageSource { + +string type + +string media_type + +string data + } + AnthropicImageBlock *-- AnthropicImageSource +``` + +## Properties + +| Name | Type | Description | +| ---- | ---- | ----------- | +| type | string | The content block type | +| source | [AnthropicImageSource](../anthropicimagesource/) | The image source (base64-encoded) | + +## Composed Types + +The following types are composed within `AnthropicImageBlock`: + +- [AnthropicImageSource](../anthropicimagesource/) diff --git a/web/src/content/docs/reference/AnthropicImageSource.md b/web/src/content/docs/reference/AnthropicImageSource.md new file mode 100644 index 00000000..9da5252c --- /dev/null +++ b/web/src/content/docs/reference/AnthropicImageSource.md @@ -0,0 +1,41 @@ +--- +title: "AnthropicImageSource" +description: "Documentation for the AnthropicImageSource type." +slug: "reference/anthropicimagesource" +--- + +Source descriptor for an Anthropic base64 image. + +## Class Diagram + +```mermaid +--- +title: AnthropicImageSource +config: + look: handDrawn + theme: colorful + class: + hideEmptyMembersBox: true +--- +classDiagram + class AnthropicImageSource { + +string type + +string media_type + +string data + } +``` + +## Yaml Example + +```yaml +media_type: image/png +data: iVBORw0KGgo... +``` + +## Properties + +| Name | Type | Description | +| ---- | ---- | ----------- | +| type | string | The encoding type (always 'base64' for inline images) | +| media_type | string | The MIME type of the image (e.g., 'image/png', 'image/jpeg') | +| data | string | The base64-encoded image data | diff --git a/web/src/content/docs/reference/AnthropicMessagesRequest.md b/web/src/content/docs/reference/AnthropicMessagesRequest.md new file mode 100644 index 00000000..456f83f2 --- /dev/null +++ b/web/src/content/docs/reference/AnthropicMessagesRequest.md @@ -0,0 +1,80 @@ +--- +title: "AnthropicMessagesRequest" +description: "Documentation for the AnthropicMessagesRequest type." +slug: "reference/anthropicmessagesrequest" +--- + +The full request body for the Anthropic Messages API (§7.5). + +## Class Diagram + +```mermaid +--- +title: AnthropicMessagesRequest +config: + look: handDrawn + theme: colorful + class: + hideEmptyMembersBox: true +--- +classDiagram + class AnthropicMessagesRequest { + +string model + +AnthropicWireMessage[] messages + +int32 max_tokens + +string system + +float32 temperature + +float32 top_p + +int32 top_k + +string[] stop_sequences + +AnthropicToolDefinition[] tools + } + class AnthropicWireMessage { + +string role + +unknown[] content + } + AnthropicMessagesRequest *-- AnthropicWireMessage + class AnthropicToolDefinition { + +string name + +string description + +dictionary input_schema + } + AnthropicMessagesRequest *-- AnthropicToolDefinition +``` + +## Yaml Example + +```yaml +model: claude-sonnet-4-20250514 +max_tokens: 4096 +system: You are a helpful assistant. +temperature: 0.7 +top_p: 0.9 +top_k: 40 +stop_sequences: + - |- + + + Human: +``` + +## Properties + +| Name | Type | Description | +| ---- | ---- | ----------- | +| model | string | The model identifier | +| messages | [AnthropicWireMessage[]](../anthropicwiremessage/) | The non-system messages to send | +| max_tokens | int32 | Maximum number of tokens to generate (required by Anthropic) | +| system | string | System prompt text (extracted from system-role messages) | +| temperature | float32 | Sampling temperature | +| top_p | float32 | Top-P sampling value | +| top_k | int32 | Top-K sampling value | +| stop_sequences | string[] | Stop sequences to end generation | +| tools | [AnthropicToolDefinition[]](../anthropictooldefinition/) | Tool definitions available to the model | + +## Composed Types + +The following types are composed within `AnthropicMessagesRequest`: + +- [AnthropicWireMessage](../anthropicwiremessage/) +- [AnthropicToolDefinition](../anthropictooldefinition/) diff --git a/web/src/content/docs/reference/AnthropicMessagesResponse.md b/web/src/content/docs/reference/AnthropicMessagesResponse.md new file mode 100644 index 00000000..c5e633d0 --- /dev/null +++ b/web/src/content/docs/reference/AnthropicMessagesResponse.md @@ -0,0 +1,61 @@ +--- +title: "AnthropicMessagesResponse" +description: "Documentation for the AnthropicMessagesResponse type." +slug: "reference/anthropicmessagesresponse" +--- + +The response body from the Anthropic Messages API. + +## Class Diagram + +```mermaid +--- +title: AnthropicMessagesResponse +config: + look: handDrawn + theme: colorful + class: + hideEmptyMembersBox: true +--- +classDiagram + class AnthropicMessagesResponse { + +string id + +string type + +string role + +unknown[] content + +string model + +string stop_reason + +AnthropicUsage usage + } + class AnthropicUsage { + +int32 input_tokens + +int32 output_tokens + } + AnthropicMessagesResponse *-- AnthropicUsage +``` + +## Yaml Example + +```yaml +id: msg_01XFDUDYJgAACzvnptvVoYEL +model: claude-sonnet-4-20250514 +stop_reason: end_turn +``` + +## Properties + +| Name | Type | Description | +| ---- | ---- | ----------- | +| id | string | Unique response identifier | +| type | string | Object type (always 'message') | +| role | string | The role of the response (always 'assistant') | +| content | unknown[] | Array of content blocks in the response (AnthropicTextBlock | AnthropicToolUseBlock) | +| model | string | The model that generated the response | +| stop_reason | string | The reason generation stopped ('end_turn', 'max_tokens', 'stop_sequence', 'tool_use') | +| usage | [AnthropicUsage](../anthropicusage/) | Token usage statistics | + +## Composed Types + +The following types are composed within `AnthropicMessagesResponse`: + +- [AnthropicUsage](../anthropicusage/) diff --git a/web/src/content/docs/reference/AnthropicTextBlock.md b/web/src/content/docs/reference/AnthropicTextBlock.md new file mode 100644 index 00000000..bef64872 --- /dev/null +++ b/web/src/content/docs/reference/AnthropicTextBlock.md @@ -0,0 +1,38 @@ +--- +title: "AnthropicTextBlock" +description: "Documentation for the AnthropicTextBlock type." +slug: "reference/anthropictextblock" +--- + +A text content block in Anthropic's array-of-blocks message format. + +## Class Diagram + +```mermaid +--- +title: AnthropicTextBlock +config: + look: handDrawn + theme: colorful + class: + hideEmptyMembersBox: true +--- +classDiagram + class AnthropicTextBlock { + +string type + +string text + } +``` + +## Yaml Example + +```yaml +text: Hello, how can I help? +``` + +## Properties + +| Name | Type | Description | +| ---- | ---- | ----------- | +| type | string | The content block type | +| text | string | The text content | diff --git a/web/src/content/docs/reference/AnthropicToolDefinition.md b/web/src/content/docs/reference/AnthropicToolDefinition.md new file mode 100644 index 00000000..bd7afa46 --- /dev/null +++ b/web/src/content/docs/reference/AnthropicToolDefinition.md @@ -0,0 +1,43 @@ +--- +title: "AnthropicToolDefinition" +description: "Documentation for the AnthropicToolDefinition type." +slug: "reference/anthropictooldefinition" +--- + +A tool definition in Anthropic's format. Unlike OpenAI which wraps +tools in `{type: "function", function: {...}}`, Anthropic uses a +flat structure with `input_schema` (§7.5). + +## Class Diagram + +```mermaid +--- +title: AnthropicToolDefinition +config: + look: handDrawn + theme: colorful + class: + hideEmptyMembersBox: true +--- +classDiagram + class AnthropicToolDefinition { + +string name + +string description + +dictionary input_schema + } +``` + +## Yaml Example + +```yaml +name: get_weather +description: Get the current weather for a city +``` + +## Properties + +| Name | Type | Description | +| ---- | ---- | ----------- | +| name | string | The tool name | +| description | string | A description of what the tool does | +| input_schema | dictionary | JSON Schema describing the tool's input parameters | diff --git a/web/src/content/docs/reference/AnthropicToolResultBlock.md b/web/src/content/docs/reference/AnthropicToolResultBlock.md new file mode 100644 index 00000000..775f14c5 --- /dev/null +++ b/web/src/content/docs/reference/AnthropicToolResultBlock.md @@ -0,0 +1,41 @@ +--- +title: "AnthropicToolResultBlock" +description: "Documentation for the AnthropicToolResultBlock type." +slug: "reference/anthropictoolresultblock" +--- + +A tool result content block sent back to the API with the tool's output. + +## Class Diagram + +```mermaid +--- +title: AnthropicToolResultBlock +config: + look: handDrawn + theme: colorful + class: + hideEmptyMembersBox: true +--- +classDiagram + class AnthropicToolResultBlock { + +string type + +string tool_use_id + +string content + } +``` + +## Yaml Example + +```yaml +tool_use_id: toolu_01A09q90qw90lq917835lq9 +content: 72°F and sunny in Paris +``` + +## Properties + +| Name | Type | Description | +| ---- | ---- | ----------- | +| type | string | The content block type | +| tool_use_id | string | The tool_use id this result corresponds to | +| content | string | The tool's output content | diff --git a/web/src/content/docs/reference/AnthropicToolUseBlock.md b/web/src/content/docs/reference/AnthropicToolUseBlock.md new file mode 100644 index 00000000..9643ef4d --- /dev/null +++ b/web/src/content/docs/reference/AnthropicToolUseBlock.md @@ -0,0 +1,46 @@ +--- +title: "AnthropicToolUseBlock" +description: "Documentation for the AnthropicToolUseBlock type." +slug: "reference/anthropictooluseblock" +--- + +A tool use content block returned in an assistant message when +the model wants to invoke a tool. + +## Class Diagram + +```mermaid +--- +title: AnthropicToolUseBlock +config: + look: handDrawn + theme: colorful + class: + hideEmptyMembersBox: true +--- +classDiagram + class AnthropicToolUseBlock { + +string type + +string id + +string name + +dictionary input + } +``` + +## Yaml Example + +```yaml +id: toolu_01A09q90qw90lq917835lq9 +name: get_weather +input: + city: Paris +``` + +## Properties + +| Name | Type | Description | +| ---- | ---- | ----------- | +| type | string | The content block type | +| id | string | Unique identifier for this tool invocation | +| name | string | The name of the tool to invoke | +| input | dictionary | The JSON arguments for the tool call | diff --git a/web/src/content/docs/reference/AnthropicUsage.md b/web/src/content/docs/reference/AnthropicUsage.md new file mode 100644 index 00000000..c1ae0344 --- /dev/null +++ b/web/src/content/docs/reference/AnthropicUsage.md @@ -0,0 +1,39 @@ +--- +title: "AnthropicUsage" +description: "Documentation for the AnthropicUsage type." +slug: "reference/anthropicusage" +--- + +Usage statistics returned in an Anthropic Messages API response. + +## Class Diagram + +```mermaid +--- +title: AnthropicUsage +config: + look: handDrawn + theme: colorful + class: + hideEmptyMembersBox: true +--- +classDiagram + class AnthropicUsage { + +int32 input_tokens + +int32 output_tokens + } +``` + +## Yaml Example + +```yaml +input_tokens: 150 +output_tokens: 42 +``` + +## Properties + +| Name | Type | Description | +| ---- | ---- | ----------- | +| input_tokens | int32 | Number of input tokens consumed | +| output_tokens | int32 | Number of output tokens generated | diff --git a/web/src/content/docs/reference/AnthropicWireMessage.md b/web/src/content/docs/reference/AnthropicWireMessage.md new file mode 100644 index 00000000..fff3ce9f --- /dev/null +++ b/web/src/content/docs/reference/AnthropicWireMessage.md @@ -0,0 +1,40 @@ +--- +title: "AnthropicWireMessage" +description: "Documentation for the AnthropicWireMessage type." +slug: "reference/anthropicwiremessage" +--- + +A single message in the Anthropic Messages API wire format. +Anthropic always uses the array-of-blocks form for content, +even when there is only one text block (§7.5). + +## Class Diagram + +```mermaid +--- +title: AnthropicWireMessage +config: + look: handDrawn + theme: colorful + class: + hideEmptyMembersBox: true +--- +classDiagram + class AnthropicWireMessage { + +string role + +unknown[] content + } +``` + +## Yaml Example + +```yaml +role: user +``` + +## Properties + +| Name | Type | Description | +| ---- | ---- | ----------- | +| role | string | The message role ('user' or 'assistant') | +| content | unknown[] | Array of typed content blocks (AnthropicTextBlock | AnthropicImageBlock | AnthropicToolUseBlock | AnthropicToolResultBlock) | diff --git a/web/src/content/docs/reference/FileNotFoundError.md b/web/src/content/docs/reference/FileNotFoundError.md new file mode 100644 index 00000000..f4603cc1 --- /dev/null +++ b/web/src/content/docs/reference/FileNotFoundError.md @@ -0,0 +1,40 @@ +--- +title: "FileNotFoundError" +description: "Documentation for the FileNotFoundError type." +slug: "reference/filenotfounderror" +--- + +Raised when a referenced file cannot be found. This applies to both +.prompty files and ${file:path} references in frontmatter. + +## Class Diagram + +```mermaid +--- +title: FileNotFoundError +config: + look: handDrawn + theme: colorful + class: + hideEmptyMembersBox: true +--- +classDiagram + class FileNotFoundError { + +string message + +string path + } +``` + +## Yaml Example + +```yaml +message: "Prompty file not found: ./chat.prompty" +path: ./chat.prompty +``` + +## Properties + +| Name | Type | Description | +| ---- | ---- | ----------- | +| message | string | Human-readable error message | +| path | string | The file path that could not be resolved | diff --git a/web/src/content/docs/reference/InvokerError.md b/web/src/content/docs/reference/InvokerError.md new file mode 100644 index 00000000..f9ace241 --- /dev/null +++ b/web/src/content/docs/reference/InvokerError.md @@ -0,0 +1,44 @@ +--- +title: "InvokerError" +description: "Documentation for the InvokerError type." +slug: "reference/invokererror" +--- + +Raised when no invoker implementation is registered for a given component +and key. For example, if no renderer is registered for the key "jinja2", +an InvokerError is raised. + +## Class Diagram + +```mermaid +--- +title: InvokerError +config: + look: handDrawn + theme: colorful + class: + hideEmptyMembersBox: true +--- +classDiagram + class InvokerError { + +string message + +string component + +string key + } +``` + +## Yaml Example + +```yaml +message: "No renderer registered for key: jinja2" +component: renderer +key: jinja2 +``` + +## Properties + +| Name | Type | Description | +| ---- | ---- | ----------- | +| message | string | Human-readable error message | +| component | string | The pipeline component type that was being looked up (e.g., 'renderer', 'parser', 'executor', 'processor') | +| key | string | The registration key that was not found | diff --git a/web/src/content/docs/reference/StreamOptions.md b/web/src/content/docs/reference/StreamOptions.md new file mode 100644 index 00000000..6992d51a --- /dev/null +++ b/web/src/content/docs/reference/StreamOptions.md @@ -0,0 +1,37 @@ +--- +title: "StreamOptions" +description: "Documentation for the StreamOptions type." +slug: "reference/streamoptions" +--- + +Options controlling streaming behavior for LLM API calls. +Passed alongside the model options when streaming is enabled. + +## Class Diagram + +```mermaid +--- +title: StreamOptions +config: + look: handDrawn + theme: colorful + class: + hideEmptyMembersBox: true +--- +classDiagram + class StreamOptions { + +boolean includeUsage + } +``` + +## Yaml Example + +```yaml +includeUsage: true +``` + +## Properties + +| Name | Type | Description | +| ---- | ---- | ----------- | +| includeUsage | boolean | When true, the final streaming chunk includes token usage statistics | diff --git a/web/src/content/docs/reference/TraceFile.md b/web/src/content/docs/reference/TraceFile.md new file mode 100644 index 00000000..cf4501f7 --- /dev/null +++ b/web/src/content/docs/reference/TraceFile.md @@ -0,0 +1,59 @@ +--- +title: "TraceFile" +description: "Documentation for the TraceFile type." +slug: "reference/tracefile" +--- + +The top-level .tracy file structure written by the file backend (§3.6.1). + +## Class Diagram + +```mermaid +--- +title: TraceFile +config: + look: handDrawn + theme: colorful + class: + hideEmptyMembersBox: true +--- +classDiagram + class TraceFile { + +string runtime + +string version + +TraceSpan trace + } + class TraceSpan { + +string name + +TraceTime __time + +string signature + +dictionary inputs + +unknown output + +string error + +TokenUsage __usage + +dictionary attributes + +unknown[] __frames + } + TraceFile *-- TraceSpan +``` + +## Yaml Example + +```yaml +runtime: python +version: 2.0.0 +``` + +## Properties + +| Name | Type | Description | +| ---- | ---- | ----------- | +| runtime | string | Language/runtime name (e.g., 'python', 'csharp', 'javascript') | +| version | string | Prompty library version | +| trace | [TraceSpan](../tracespan/) | The root trace span | + +## Composed Types + +The following types are composed within `TraceFile`: + +- [TraceSpan](../tracespan/) diff --git a/web/src/content/docs/reference/TraceSpan.md b/web/src/content/docs/reference/TraceSpan.md new file mode 100644 index 00000000..cd1abf55 --- /dev/null +++ b/web/src/content/docs/reference/TraceSpan.md @@ -0,0 +1,75 @@ +--- +title: "TraceSpan" +description: "Documentation for the TraceSpan type." +slug: "reference/tracespan" +--- + +A single trace span capturing one pipeline stage or function invocation. +Spans nest via the `__frames` field to form a tree representing the +full execution (§3.6.1). + +## Class Diagram + +```mermaid +--- +title: TraceSpan +config: + look: handDrawn + theme: colorful + class: + hideEmptyMembersBox: true +--- +classDiagram + class TraceSpan { + +string name + +TraceTime __time + +string signature + +dictionary inputs + +unknown output + +string error + +TokenUsage __usage + +dictionary attributes + +unknown[] __frames + } + class TraceTime { + +string start + +string end + +float64 duration + } + TraceSpan *-- TraceTime + class TokenUsage { + +int32 promptTokens + +int32 completionTokens + +int32 totalTokens + } + TraceSpan *-- TokenUsage +``` + +## Yaml Example + +```yaml +name: prompty.core.pipeline.run +signature: prompty.core.pipeline.run +error: Connection refused +``` + +## Properties + +| Name | Type | Description | +| ---- | ---- | ----------- | +| name | string | The name of this span (typically the function signature) | +| __time | [TraceTime](../tracetime/) | Timing information for this span | +| signature | string | Fully-qualified function signature that produced this span | +| inputs | dictionary | Serialized input parameters (redacted per §3.4) | +| output | unknown | Serialized return value or error information (redacted per §3.4) | +| error | string | Error message if the span ended with an exception | +| __usage | [TokenUsage](../tokenusage/) | Aggregated token usage hoisted from child spans (§3.5) | +| attributes | dictionary | Additional span attributes (e.g., OpenTelemetry GenAI attributes) | +| __frames | unknown[] | Nested child spans forming the execution tree (recursive; each element is a TraceSpan) | + +## Composed Types + +The following types are composed within `TraceSpan`: + +- [TraceTime](../tracetime/) +- [TokenUsage](../tokenusage/) diff --git a/web/src/content/docs/reference/TraceTime.md b/web/src/content/docs/reference/TraceTime.md new file mode 100644 index 00000000..f48230e9 --- /dev/null +++ b/web/src/content/docs/reference/TraceTime.md @@ -0,0 +1,42 @@ +--- +title: "TraceTime" +description: "Documentation for the TraceTime type." +slug: "reference/tracetime" +--- + +Timing information for a trace span. + +## Class Diagram + +```mermaid +--- +title: TraceTime +config: + look: handDrawn + theme: colorful + class: + hideEmptyMembersBox: true +--- +classDiagram + class TraceTime { + +string start + +string end + +float64 duration + } +``` + +## Yaml Example + +```yaml +start: 2026-04-04T12:00:00Z +end: 2026-04-04T12:00:01Z +duration: 1000 +``` + +## Properties + +| Name | Type | Description | +| ---- | ---- | ----------- | +| start | string | ISO 8601 UTC timestamp when the span started | +| end | string | ISO 8601 UTC timestamp when the span ended | +| duration | float64 | Duration of the span in milliseconds | diff --git a/web/src/content/docs/reference/ValidationError.md b/web/src/content/docs/reference/ValidationError.md new file mode 100644 index 00000000..bfca31c4 --- /dev/null +++ b/web/src/content/docs/reference/ValidationError.md @@ -0,0 +1,43 @@ +--- +title: "ValidationError" +description: "Documentation for the ValidationError type." +slug: "reference/validationerror" +--- + +Raised when input validation fails. Each ValidationError describes a +single property that did not satisfy its constraint. + +## Class Diagram + +```mermaid +--- +title: ValidationError +config: + look: handDrawn + theme: colorful + class: + hideEmptyMembersBox: true +--- +classDiagram + class ValidationError { + +string message + +string property + +string constraint + } +``` + +## Yaml Example + +```yaml +message: "Missing required input: firstName" +property: firstName +constraint: required +``` + +## Properties + +| Name | Type | Description | +| ---- | ---- | ----------- | +| message | string | Human-readable error message | +| property | string | The name of the property that failed validation | +| constraint | string | The constraint that was violated (e.g., 'required', 'type') | diff --git a/web/src/content/docs/reference/ValidationResult.md b/web/src/content/docs/reference/ValidationResult.md new file mode 100644 index 00000000..7c8fabe2 --- /dev/null +++ b/web/src/content/docs/reference/ValidationResult.md @@ -0,0 +1,53 @@ +--- +title: "ValidationResult" +description: "Documentation for the ValidationResult type." +slug: "reference/validationresult" +--- + +The result of validating inputs against an agent's inputSchema. +Returned by `validate_inputs` (§12.2) to indicate whether all +required inputs are present and satisfy their constraints. + +## Class Diagram + +```mermaid +--- +title: ValidationResult +config: + look: handDrawn + theme: colorful + class: + hideEmptyMembersBox: true +--- +classDiagram + class ValidationResult { + +boolean valid + +ValidationError[] errors + } + class ValidationError { + +string message + +string property + +string constraint + } + ValidationResult *-- ValidationError +``` + +## Yaml Example + +```yaml +valid: true +errors: [] +``` + +## Properties + +| Name | Type | Description | +| ---- | ---- | ----------- | +| valid | boolean | Whether all inputs passed validation | +| errors | [ValidationError[]](../validationerror/) | List of validation errors (empty when valid is true) | + +## Composed Types + +The following types are composed within `ValidationResult`: + +- [ValidationError](../validationerror/) diff --git a/web/src/content/docs/reference/index.md b/web/src/content/docs/reference/index.md index ba5baab1..084000ac 100644 --- a/web/src/content/docs/reference/index.md +++ b/web/src/content/docs/reference/index.md @@ -1,7 +1,7 @@ --- title: "AgentSchema" description: "Overview of declarative agent types in AgentSchema." -slug: "reference/index" +slug: "reference" sidebar: order: 1 ---