C# RHINOCOMPUTE plugin clarifications

Why does this work correctly

My actual working code :

using Rhino;
using Rhino.Geometry;
using Rhino.DocObjects;
using System;
using System.IO;
using Rhino.FileIO;
namespace HelloRhinoCompute
{
    public static class Convert3dm
    {
        public static string ConvertToStl(string data, bool exportAsBinary = true)
        {
            try
            {
                var decodedData = Convert.FromBase64String(data);
                var file3dm = File3dm.FromByteArray(decodedData);
                // Create a new document for exporting
                using (var exportDoc = RhinoDoc.CreateHeadless(null))
                {
                    // Add all objects to the export document
                    foreach (var obj in file3dm.Objects)
                    {
                        var geometry = obj.Geometry;
                        var attributes = obj.Attributes;
                        exportDoc.Objects.Add(geometry, attributes);
                    }
                    // Define the STL export options
                    var stlOptions = new FileStlWriteOptions
                    {
                        BinaryFile = exportAsBinary,
                        ExportOpenObjects = true,
                        MeshingParameters = MeshingParameters.Default
                    };
                    // Export to STL
                    var id = Guid.NewGuid();
                    string path = Path.GetTempPath();
                    string filename = Path.Combine(path, $"convert3dm_{id}.stl");
                    if (!FileStl.Write(filename, exportDoc, stlOptions))
                    {
                        throw new InvalidOperationException("STL export failed");
                    }
                    byte[] bytes = File.ReadAllBytes(filename);
                    // Clean up the temporary file
                    File.Delete(filename);
                    return Convert.ToBase64String(bytes);
                }
            }
            catch (Exception ex)
            {
                throw new InvalidOperationException($"Error in ConvertToStl: {ex.Message}", ex);
            }
        }

    }
}

My attempt at adding a layer filtering mechanism for model conversion, here it breaks. Need help here :

using Rhino;
using Rhino.Geometry;
using Rhino.DocObjects;
using System;
using System.IO;
using Rhino.FileIO;
namespace HelloRhinoCompute
{
    public static class Convert3dm
    {
        public static string ConvertToStl(string data, bool exportAsBinary = true)
        {
            try
            {
                var decodedData = Convert.FromBase64String(data);
                var file3dm = File3dm.FromByteArray(decodedData);
                // Create a new document for exporting
                using (var exportDoc = RhinoDoc.CreateHeadless(null))
                {
                    // Add all objects to the export document
                    foreach (var obj in file3dm.Objects)
                    {
                        var geometry = obj.Geometry;
                        var attributes = obj.Attributes;
                        exportDoc.Objects.Add(geometry, attributes);
                    }
                    // Define the STL export options
                    var stlOptions = new FileStlWriteOptions
                    {
                        BinaryFile = exportAsBinary,
                        ExportOpenObjects = true,
                        MeshingParameters = MeshingParameters.Default
                    };
                    // Export to STL
                    var id = Guid.NewGuid();
                    string path = Path.GetTempPath();
                    string filename = Path.Combine(path, $"convert3dm_{id}.stl");
                    if (!FileStl.Write(filename, exportDoc, stlOptions))
                    {
                        throw new InvalidOperationException("STL export failed");
                    }
                    byte[] bytes = File.ReadAllBytes(filename);
                    // Clean up the temporary file
                    File.Delete(filename);
                    return Convert.ToBase64String(bytes);
                }
            }
            catch (Exception ex)
            {
                throw new InvalidOperationException($"Error in ConvertToStl: {ex.Message}", ex);
            }
        }
        // You can add other conversion methods (e.g., Convert3dmToPdf, Convert3dmToDwg) here if needed
    }
}

I have this plugin on my local PC that works just fine (compiled as a plugin, not rhinocompute plugin), I need to make it into a RhinoCompute endpoint however I’m having a hard time understanding why the #2 code snippet I sent doesnt work, but the 1 does work correctly.

using Rhino;
using Rhino.Commands;
using Rhino.Geometry;
using Rhino.Input.Custom;
using Rhino.DocObjects;
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;
using Newtonsoft.Json.Linq;
using Rhino.FileIO;

public class ExportMetalObjectsOnly : Command
{
    public override string EnglishName => "ExportMetalObjectsOnly";

    protected override Result RunCommand(RhinoDoc doc, RunMode mode)
    {
        RhinoApp.WriteLine("Starting ExportMetalObjectsOnly command...");

        try
        {
            // Prompt the user to select objects
            var go = new GetObject
            {
                GroupSelect = true,
                SubObjectSelect = false
            };
            go.SetCommandPrompt("Select objects to export to STL");
            go.GetMultiple(1, 0);
            if (go.CommandResult() != Result.Success)
            {
                RhinoApp.WriteLine("Object selection failed or was cancelled.");
                return go.CommandResult();
            }
            var objectsToExport = go.Objects();

            // Check if any objects were selected
            if (objectsToExport.Length == 0)
            {
                RhinoApp.WriteLine("No objects selected. Exiting command.");
                return Result.Failure;
            }

            RhinoApp.WriteLine($"Selected {objectsToExport.Length} objects for export.");

            // Create a new document for exporting
            using (var exportDoc = RhinoDoc.CreateHeadless(null))
            {
                RhinoApp.WriteLine("Created headless document for export.");

                // Loop through selected objects and remove non-metal (gem) objects
                foreach (var objRef in objectsToExport)
                {
                    var geometry = objRef.Geometry();
                    var attributes = objRef.Object().Attributes;
                    string layerName = doc.Layers[attributes.LayerIndex].Name;

                    RhinoApp.WriteLine($"Processing object on layer: {layerName}");

                    // Classify the layer name using LLAMA model
                    bool isMetal = ClassifyMaterial(layerName);

                    if (isMetal)
                    {
                        // Add the metal object to the export document
                        var newAttributes = attributes.Duplicate();
                        exportDoc.Objects.Add(geometry, newAttributes);
                        RhinoApp.WriteLine("Object added to export document (metal).");
                    }
                    else
                    {
                        RhinoApp.WriteLine("Object skipped (gem).");
                    }

                    // Ensure API requests are limited to 1 per second
                    Thread.Sleep(1000); // 1 second delay
                }

                // Define the STL export options
                var stlOptions = new FileStlWriteOptions
                {
                    BinaryFile = true,
                    ExportOpenObjects = true,
                    MeshingParameters = MeshingParameters.Default
                };

                // Define the destination path on the desktop
                var desktopPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Desktop);
                var exportFilePath = Path.Combine(desktopPath, "ExportedMetalObjects.stl");

                RhinoApp.WriteLine($"Preparing to export to: {exportFilePath}");

                // Export to STL
                bool success = FileStl.Write(exportFilePath, exportDoc, stlOptions);

                if (success)
                {
                    RhinoApp.WriteLine($"Successfully exported to {exportFilePath}");
                    return Result.Success;
                }
                else
                {
                    RhinoApp.WriteLine("Failed to export STL file.");
                    return Result.Failure;
                }
            }
        }
        catch (Exception ex)
        {
            RhinoApp.WriteLine($"An error occurred: {ex.Message}");
            RhinoApp.WriteLine($"Stack trace: {ex.StackTrace}");
            return Result.Failure;
        }
    }

    private static bool ClassifyMaterial(string layerName)
    {
        string apiKey = "xx";
        string apiUrl = "https://api.together.xyz/v1/chat/completions";

        try
        {
            RhinoApp.WriteLine($"Classifying material by layer name: {layerName}");

            var request = (HttpWebRequest)WebRequest.Create(apiUrl);
            request.Method = "POST";
            request.ContentType = "application/json";
            request.Headers.Add("Authorization", $"Bearer {apiKey}");

            var requestBody = new
            {
                model = "meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo",
                messages = new[]
                {
                    new { role = "system", content = "You are a helpful assistant that classifies materials as either metal or gem. Respond with 1 for metal or 0 for gem." },
                    new { role = "user", content = $"Is the material represented by the layer name '{layerName}' a metal or a gem? Respond with 1 for metal or 0 for gem." }
                },
                max_tokens = 10,
                temperature = 0.7,
                top_p = 0.7,
                top_k = 50,
                repetition_penalty = 1,
                stop = new[] { "<|eot_id|>", "<|eom_id|>" }
            };

            string jsonRequest = Newtonsoft.Json.JsonConvert.SerializeObject(requestBody);

            using (var streamWriter = new StreamWriter(request.GetRequestStream()))
            {
                streamWriter.Write(jsonRequest);
            }

            using (var response = (HttpWebResponse)request.GetResponse())
            using (var streamReader = new StreamReader(response.GetResponseStream()))
            {
                string responseBody = streamReader.ReadToEnd();
                RhinoApp.WriteLine($"API Response: {responseBody}");

                var jsonResponse = JObject.Parse(responseBody);
                string assistantResponse = jsonResponse["choices"][0]["message"]["content"].ToString().Trim();
                bool isMetal = assistantResponse == "1";

                RhinoApp.WriteLine($"Classification result: {(isMetal ? "Metal" : "Gem")}");
                return isMetal;
            }
        }
        catch (WebException ex)
        {
            using (var streamReader = new StreamReader(ex.Response.GetResponseStream()))
            {
                string errorResponse = streamReader.ReadToEnd();
                RhinoApp.WriteLine($"API Error: {errorResponse}");
            }
            RhinoApp.WriteLine($"WebException occurred in ClassifyMaterial: {ex.Message}");
            return true; // Default to metal if classification fails
        }
        catch (Exception ex)
        {
            RhinoApp.WriteLine($"Error in ClassifyMaterial: {ex.Message}");
            return true; // Default to metal if classification fails
        }
    }
}

I need to make this plugin work as a endpoint basically…

Could I get some help?
Thank you guys

I solved this by using Rhino3dm for layer and object material detection, and also for scaling

However I want to learn why that would not work in RhinOCompute, is it because of headless env? I would like to know what prevents my Rhincoompute custom endpooint from working when I integrat ethe layer fitlering part.

Thanks

I will guess that RhinoApp.WriteLine would not work, but more likely it is because there is something not working when you call the llama service. I would isolate this and test that part of the code by itself. Would a simple switch case with typical metal names here not be easier than calling an llm to give you the answer?