From 69439c1added0dd7df7b6f8132e04c2af619cc21 Mon Sep 17 00:00:00 2001 From: Piotr Stachaczynski Date: Sat, 1 Nov 2025 20:14:13 +0100 Subject: [PATCH 1/3] test --- Examples/Examples.SimpleConsole/ImageArgs.cs | 14 ++ Examples/Examples.SimpleConsole/ImageTools.cs | 182 ++++++++++++++++++ Examples/Examples.SimpleConsole/Program.cs | 80 ++++++-- 3 files changed, 265 insertions(+), 11 deletions(-) create mode 100644 Examples/Examples.SimpleConsole/ImageArgs.cs create mode 100644 Examples/Examples.SimpleConsole/ImageTools.cs diff --git a/Examples/Examples.SimpleConsole/ImageArgs.cs b/Examples/Examples.SimpleConsole/ImageArgs.cs new file mode 100644 index 00000000..5e44210f --- /dev/null +++ b/Examples/Examples.SimpleConsole/ImageArgs.cs @@ -0,0 +1,14 @@ +namespace Examples.SimpleConsole; + + +public class GenerateImageArgs +{ + public string Prompt { get; set; } = string.Empty; +} + +public class EditImageArgs +{ + public string ImageUrl { get; set; } = string.Empty; + public string Prompt { get; set; } = string.Empty; +} + diff --git a/Examples/Examples.SimpleConsole/ImageTools.cs b/Examples/Examples.SimpleConsole/ImageTools.cs new file mode 100644 index 00000000..e7ee6ad9 --- /dev/null +++ b/Examples/Examples.SimpleConsole/ImageTools.cs @@ -0,0 +1,182 @@ +using System.Net.Http.Headers; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; +using Examples.SimpleConsole; + +namespace MaIN.NET.Examples.TogetherAI; + +public static class ImageTools +{ + private const string TogetherApiUrl = "https://api.together.xyz/v1/images/generations"; + private const string ImgbbApiUrl = "https://api.imgbb.com/1/upload"; + private const string ModelGenerate = "black-forest-labs/FLUX.1-schnell"; + private const string ModelEdit = "black-forest-labs/FLUX.1-kontext-dev"; + private static readonly HttpClient HttpClient = new(); + private static string? TogetherApiKey => "<>"; + private static string? ImgbbApiKey => "<>"; + + public static async Task GenerateImage(GenerateImageArgs args) + { + try + { + if (string.IsNullOrEmpty(TogetherApiKey)) + return new { error = "Set TOGETHER_API_KEY environment variable" }; + + if (string.IsNullOrEmpty(ImgbbApiKey)) + return new { error = "Set IMGBB_API_KEY environment variable" }; + + // Generate image with Together AI + var requestBody = new + { + model = ModelGenerate, + prompt = args.Prompt, + width = 1024, + height = 768, + steps = 4, + n = 1, + response_format = "b64_json" + }; + + var json = JsonSerializer.Serialize(requestBody); + var content = new StringContent(json, Encoding.UTF8, "application/json"); + + using var request = new HttpRequestMessage(HttpMethod.Post, TogetherApiUrl); + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", TogetherApiKey); + request.Content = content; + + var response = await HttpClient.SendAsync(request); + var responseContent = await response.Content.ReadAsStringAsync(); + + if (!response.IsSuccessStatusCode) + return new { error = $"Together API error: {response.StatusCode}", details = responseContent }; + + var result = JsonSerializer.Deserialize(responseContent); + if (result?.Data == null || result.Data.Length == 0) + return new { error = "No image generated" }; + + var imageBase64 = result.Data[0].B64Json; + + // Upload to imgbb + var imgbbUrl = await UploadToImgbb(imageBase64); + if (imgbbUrl == null) + return new { error = "Failed to upload to imgbb" }; + + return new + { + success = true, + url = imgbbUrl + }; + } + catch (Exception ex) + { + return new { error = ex.Message }; + } + } + + public static async Task EditImage(EditImageArgs args) + { + try + { + if (string.IsNullOrEmpty(TogetherApiKey)) + return new { error = "Set TOGETHER_API_KEY environment variable" }; + + if (string.IsNullOrEmpty(ImgbbApiKey)) + return new { error = "Set IMGBB_API_KEY environment variable" }; + + if (string.IsNullOrEmpty(args.ImageUrl)) + return new { error = "Provide ImageUrl" }; + + // Edit image with Together AI + var requestBody = new + { + model = ModelEdit, + prompt = args.Prompt, + image_url = args.ImageUrl, + steps = 4, + response_format = "b64_json" + }; + + var json = JsonSerializer.Serialize(requestBody); + var content = new StringContent(json, Encoding.UTF8, "application/json"); + + using var request = new HttpRequestMessage(HttpMethod.Post, TogetherApiUrl); + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", TogetherApiKey); + request.Content = content; + + var response = await HttpClient.SendAsync(request); + var responseContent = await response.Content.ReadAsStringAsync(); + + if (!response.IsSuccessStatusCode) + return new { error = $"Together API error: {response.StatusCode}", details = responseContent }; + + var result = JsonSerializer.Deserialize(responseContent); + if (result?.Data == null || result.Data.Length == 0) + return new { error = "No edited image returned" }; + + var imageBase64 = result.Data[0].B64Json; + + // Upload to imgbb + var imgbbUrl = await UploadToImgbb(imageBase64); + if (imgbbUrl == null) + return new { error = "Failed to upload to imgbb" }; + + return new + { + success = true, + url = imgbbUrl + }; + } + catch (Exception ex) + { + return new { error = ex.Message }; + } + } + + private static async Task UploadToImgbb(string base64Image) + { + try + { + var formData = new MultipartFormDataContent(); + formData.Add(new StringContent(ImgbbApiKey!), "key"); + formData.Add(new StringContent(base64Image), "image"); + + var response = await HttpClient.PostAsync(ImgbbApiUrl, formData); + var responseContent = await response.Content.ReadAsStringAsync(); + + if (!response.IsSuccessStatusCode) + return null; + + var result = JsonSerializer.Deserialize(responseContent); + return result?.Data?.Url; + } + catch + { + return null; + } + } +} + +public class TogetherImageResponse +{ + [JsonPropertyName("data")] + public TogetherImageData[]? Data { get; set; } +} + +public class TogetherImageData +{ + [JsonPropertyName("b64_json")] + public string B64Json { get; set; } = string.Empty; +} + +public class ImgbbResponse +{ + [JsonPropertyName("data")] + public ImgbbData? Data { get; set; } +} + +public class ImgbbData +{ + [JsonPropertyName("url")] + public string Url { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/Examples/Examples.SimpleConsole/Program.cs b/Examples/Examples.SimpleConsole/Program.cs index 3ace6527..b5d9faaf 100644 --- a/Examples/Examples.SimpleConsole/Program.cs +++ b/Examples/Examples.SimpleConsole/Program.cs @@ -1,14 +1,72 @@ -using MaIN.Core; -using MaIN.Core.Hub; -using MaIN.Domain.Entities; -using OpenAI.Models; + using Examples.SimpleConsole; + using MaIN.Core; + using MaIN.Core.Hub; + using MaIN.Core.Hub.Utils; + using MaIN.Domain.Configuration; + using MaIN.NET.Examples.TogetherAI; -MaINBootstrapper.Initialize(); - -var model = AIHub.Model(); - -var m = model.GetModel("gemma3:4b"); -var x = model.GetModel("llama3.2:3b"); -await model.DownloadAsync(x.Name); + MaINBootstrapper.Initialize(); + Console.WriteLine("Image Agent - FLUX.1 schnell (~$0.028/image)"); + Console.WriteLine("Set TOGETHER_API_KEY and IMGBB_API_KEY environment variables"); + Console.WriteLine(); + var context = await AIHub.Agent() + .WithBackend(BackendType.Anthropic) + .WithModel("claude-sonnet-4-5-20250929") + .WithSteps(StepBuilder.Instance + .Answer() + .Build()) + .WithTools(new ToolsConfigurationBuilder() + + .AddTool( + "generate_image", + "Generate an image from a text prompt. Returns imgbb URL.", + new + { + type = "object", + properties = new + { + prompt = new + { + type = "string", + description = "Description of the image to generate" + } + }, + required = new[] { "prompt" } + }, + ImageTools.GenerateImage) + + .AddTool( + "edit_image", + "Edit an existing image using its URL. When user says 'add X to it' or similar, use the URL from the previous generate/edit response in this conversation.", + new + { + type = "object", + properties = new + { + imageUrl = new + { + type = "string", + description = "URL of the image to edit (from previous generate/edit or external URL)" + }, + prompt = new + { + type = "string", + description = "How to modify the image" + } + }, + required = new[] { "imageUrl", "prompt" } + }, + ImageTools.EditImage) + + .WithToolChoice("auto") + .Build()) + .CreateAsync(interactiveResponse: true); + // Example: Natural conversation flow with URLs + await context.ProcessAsync("Generate a cat"); + + Console.WriteLine("\n--//--\n"); + + // Claude should remember the URL from above and use it here + await context.ProcessAsync("Nice! Now add a wizard hat to it"); From 104904ab00ffbd935e789ea1db5a4d7ef016b676 Mon Sep 17 00:00:00 2001 From: Piotr Stachaczynski Date: Mon, 3 Nov 2025 19:01:16 +0100 Subject: [PATCH 2/3] f: test --- Examples/Examples.SimpleConsole/ImageTools.cs | 45 +++++++++++-------- Examples/Examples.SimpleConsole/Program.cs | 2 - 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/Examples/Examples.SimpleConsole/ImageTools.cs b/Examples/Examples.SimpleConsole/ImageTools.cs index e7ee6ad9..479a0c8b 100644 --- a/Examples/Examples.SimpleConsole/ImageTools.cs +++ b/Examples/Examples.SimpleConsole/ImageTools.cs @@ -10,8 +10,7 @@ public static class ImageTools { private const string TogetherApiUrl = "https://api.together.xyz/v1/images/generations"; private const string ImgbbApiUrl = "https://api.imgbb.com/1/upload"; - private const string ModelGenerate = "black-forest-labs/FLUX.1-schnell"; - private const string ModelEdit = "black-forest-labs/FLUX.1-kontext-dev"; + private const string Model = "black-forest-labs/FLUX.1-schnell"; private static readonly HttpClient HttpClient = new(); private static string? TogetherApiKey => "<>"; private static string? ImgbbApiKey => "<>"; @@ -22,14 +21,14 @@ public static async Task GenerateImage(GenerateImageArgs args) { if (string.IsNullOrEmpty(TogetherApiKey)) return new { error = "Set TOGETHER_API_KEY environment variable" }; - + if (string.IsNullOrEmpty(ImgbbApiKey)) return new { error = "Set IMGBB_API_KEY environment variable" }; // Generate image with Together AI var requestBody = new { - model = ModelGenerate, + model = Model, prompt = args.Prompt, width = 1024, height = 768, @@ -80,20 +79,33 @@ public static async Task EditImage(EditImageArgs args) { if (string.IsNullOrEmpty(TogetherApiKey)) return new { error = "Set TOGETHER_API_KEY environment variable" }; - + if (string.IsNullOrEmpty(ImgbbApiKey)) return new { error = "Set IMGBB_API_KEY environment variable" }; if (string.IsNullOrEmpty(args.ImageUrl)) return new { error = "Provide ImageUrl" }; - // Edit image with Together AI + string imageBase64; + try + { + var imageBytes = await HttpClient.GetByteArrayAsync(args.ImageUrl); + imageBase64 = Convert.ToBase64String(imageBytes); + } + catch (Exception ex) + { + return new { error = $"Failed to download image: {ex.Message}" }; + } + var requestBody = new { - model = ModelEdit, + model = Model, prompt = args.Prompt, - image_url = args.ImageUrl, + image = imageBase64, + width = 1024, + height = 768, steps = 4, + n = 1, response_format = "b64_json" }; @@ -114,10 +126,9 @@ public static async Task EditImage(EditImageArgs args) if (result?.Data == null || result.Data.Length == 0) return new { error = "No edited image returned" }; - var imageBase64 = result.Data[0].B64Json; + var editedImageBase64 = result.Data[0].B64Json; - // Upload to imgbb - var imgbbUrl = await UploadToImgbb(imageBase64); + var imgbbUrl = await UploadToImgbb(editedImageBase64); if (imgbbUrl == null) return new { error = "Failed to upload to imgbb" }; @@ -159,24 +170,20 @@ public static async Task EditImage(EditImageArgs args) public class TogetherImageResponse { - [JsonPropertyName("data")] - public TogetherImageData[]? Data { get; set; } + [JsonPropertyName("data")] public TogetherImageData[]? Data { get; set; } } public class TogetherImageData { - [JsonPropertyName("b64_json")] - public string B64Json { get; set; } = string.Empty; + [JsonPropertyName("b64_json")] public string B64Json { get; set; } = string.Empty; } public class ImgbbResponse { - [JsonPropertyName("data")] - public ImgbbData? Data { get; set; } + [JsonPropertyName("data")] public ImgbbData? Data { get; set; } } public class ImgbbData { - [JsonPropertyName("url")] - public string Url { get; set; } = string.Empty; + [JsonPropertyName("url")] public string Url { get; set; } = string.Empty; } \ No newline at end of file diff --git a/Examples/Examples.SimpleConsole/Program.cs b/Examples/Examples.SimpleConsole/Program.cs index b5d9faaf..8bc8f394 100644 --- a/Examples/Examples.SimpleConsole/Program.cs +++ b/Examples/Examples.SimpleConsole/Program.cs @@ -17,7 +17,6 @@ .Answer() .Build()) .WithTools(new ToolsConfigurationBuilder() - .AddTool( "generate_image", "Generate an image from a text prompt. Returns imgbb URL.", @@ -35,7 +34,6 @@ required = new[] { "prompt" } }, ImageTools.GenerateImage) - .AddTool( "edit_image", "Edit an existing image using its URL. When user says 'add X to it' or similar, use the URL from the previous generate/edit response in this conversation.", From fd0008adfe2f05608daf590fcedd42ec630d12ad Mon Sep 17 00:00:00 2001 From: Piotr Stachaczynski Date: Tue, 4 Nov 2025 12:33:07 +0100 Subject: [PATCH 3/3] f: tests --- Examples/Examples.SimpleConsole/ImageTools.cs | 24 +++++-------------- Examples/Examples.SimpleConsole/Program.cs | 4 ++-- 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/Examples/Examples.SimpleConsole/ImageTools.cs b/Examples/Examples.SimpleConsole/ImageTools.cs index 479a0c8b..37fba557 100644 --- a/Examples/Examples.SimpleConsole/ImageTools.cs +++ b/Examples/Examples.SimpleConsole/ImageTools.cs @@ -32,7 +32,7 @@ public static async Task GenerateImage(GenerateImageArgs args) prompt = args.Prompt, width = 1024, height = 768, - steps = 4, + steps = 10, n = 1, response_format = "b64_json" }; @@ -85,28 +85,16 @@ public static async Task EditImage(EditImageArgs args) if (string.IsNullOrEmpty(args.ImageUrl)) return new { error = "Provide ImageUrl" }; - - string imageBase64; - try - { - var imageBytes = await HttpClient.GetByteArrayAsync(args.ImageUrl); - imageBase64 = Convert.ToBase64String(imageBytes); - } - catch (Exception ex) - { - return new { error = $"Failed to download image: {ex.Message}" }; - } + var requestBody = new { - model = Model, - prompt = args.Prompt, - image = imageBase64, + model = "black-forest-labs/FLUX.1-kontext-dev", + prompt = "make this dragon with cat head", + image_url = "https://ik.imagekit.io/g3m8563jj/together-image.png?updatedAt=1762246376998", width = 1024, height = 768, - steps = 4, - n = 1, - response_format = "b64_json" + //response_format = "b64_json" }; var json = JsonSerializer.Serialize(requestBody); diff --git a/Examples/Examples.SimpleConsole/Program.cs b/Examples/Examples.SimpleConsole/Program.cs index 8bc8f394..fbb83689 100644 --- a/Examples/Examples.SimpleConsole/Program.cs +++ b/Examples/Examples.SimpleConsole/Program.cs @@ -11,8 +11,8 @@ Console.WriteLine(); var context = await AIHub.Agent() - .WithBackend(BackendType.Anthropic) - .WithModel("claude-sonnet-4-5-20250929") + .WithBackend(BackendType.GroqCloud) + .WithModel("meta-llama/llama-4-maverick-17b-128e-instruct") .WithSteps(StepBuilder.Instance .Answer() .Build())