sgprocessing/sg_processing_bridge.cpp¶
SGProcessingManager bridge — Phase 1 direct inference. More...
Namespaces¶
| Name |
|---|
| sgns |
| sgns::neoswarm |
| sgns::neoswarm::core |
Detailed Description¶
SGProcessingManager bridge — Phase 1 direct inference.
Date: 2026-05-06
JSON schema matches SuperGenius/test/src/processing_datatypes/ examples exactly.
Source code¶
#include "sg_processing_bridge.hpp"
#include "common/logging.hpp"
#include "network/sg_client/super_genius_client.hpp"
#include <boost/asio/io_context.hpp>
#include <nlohmann/json.hpp>
#include <filesystem>
#include <Generators.hpp>
#include <InputFormat.hpp>
#include <SGNSProcMain.hpp>
#include <processingbase/ProcessingManager.hpp>
// namespace sgns
namespace sgns::neoswarm::core
{
namespace
{
auto BridgeLogger()
{
return neoswarm::CreateLogger( "SGProcessingBridge" );
}
// -----------------------------------------------------------------------
// InputFormat → JSON type string
// Maps to the "type" field in the inputs array of the GNUS schema.
// These match the DataType enum values used by SGProcessingManager.
// -----------------------------------------------------------------------
std::string InputFormatToTypeString( sgns::InputFormat fmt )
{
switch ( fmt )
{
case sgns::InputFormat::FLOAT32:
return "float";
case sgns::InputFormat::FLOAT16:
return "float";
case sgns::InputFormat::INT32:
return "int";
case sgns::InputFormat::INT8:
return "int";
case sgns::InputFormat::INT16:
return "int";
case sgns::InputFormat::RGB8:
return "texture2d";
case sgns::InputFormat::RGBA8:
return "texture2d";
case sgns::InputFormat::FP4_ULTRA:
return "fp4_ultra"; // FP4_ULTRA → dedicated processor
default:
return "tensor";
}
}
// -----------------------------------------------------------------------
// InputFormat → JSON format string
// Maps to the "format" field in the inputs array.
// -----------------------------------------------------------------------
std::string InputFormatToFormatString( sgns::InputFormat fmt )
{
switch ( fmt )
{
case sgns::InputFormat::FLOAT16:
return "FLOAT16";
case sgns::InputFormat::FLOAT32:
return "FLOAT32";
case sgns::InputFormat::INT16:
return "INT16";
case sgns::InputFormat::INT32:
return "INT32";
case sgns::InputFormat::INT8:
return "INT8";
case sgns::InputFormat::RGB8:
return "RGB8";
case sgns::InputFormat::RGBA8:
return "RGBA8";
case sgns::InputFormat::FP4_ULTRA:
return "FP4_ULTRA";
default:
return "FLOAT32";
}
}
// -----------------------------------------------------------------------
// Ensure a URI is absolute (file:///absolute/path).
// SGProcessingManager requires absolute file:// URIs.
// Relative paths like "file://data/input.bin" are patched to absolute.
// -----------------------------------------------------------------------
std::string EnsureAbsoluteUri( const std::string& uri )
{
// Already absolute: file:///path or file://C:/path
if ( uri.find( "file:///" ) == 0 || uri.find( "ipfs://" ) == 0 )
{
return uri;
}
// Relative file:// URI — prepend cwd
if ( uri.find( "file://" ) == 0 )
{
const std::string rel = uri.substr( 7 ); // strip "file://"
// Use the path as-is if it looks absolute already
if ( !rel.empty() && ( rel[0] == '/' || ( rel.size() > 1 && rel[1] == ':' ) ) )
{
return uri;
}
// Prepend current working directory
std::string cwd = std::filesystem::current_path().string();
if ( !cwd.empty() )
{
return std::string( "file://" ) + cwd + "/" + rel;
}
}
return uri;
}
} // namespace
SGProcessingBridge::SGProcessingBridge()
: m_cfg( {} )
{
}
SGProcessingBridge::SGProcessingBridge( Config cfg )
: m_cfg( std::move( cfg ) )
{
}
// -----------------------------------------------------------------------
// BuildSchemaJson
//
// Produces JSON matching the exact schema used by SGProcessingManager,
// as documented in SuperGenius/test/src/processing_datatypes/*.json.
// -----------------------------------------------------------------------
outcome::result<std::string> SGProcessingBridge::BuildSchemaJson( const std::string& model_uri,
const std::string& input_uri,
sgns::InputFormat input_format,
const std::vector<int64_t>& shape ) const
{
if ( model_uri.empty() || input_uri.empty() )
{
return outcome::failure( Error::InvalidArgument );
}
const std::string type_str = InputFormatToTypeString( input_format );
const std::string format_str = InputFormatToFormatString( input_format );
// Compute flat width from shape (product of all dims)
int64_t flat_width = 1;
for ( const auto& dim : shape )
{
if ( dim > 0 )
flat_width *= dim;
}
// Build shape array for input_nodes and output_nodes
nlohmann::json shape_json = nlohmann::json::array();
for ( const auto& dim : shape )
shape_json.push_back( dim );
// Chunking parameters — block_len is the chunk size for processing,
// chunk_stride is the step between chunks.
// For LLM inference: block_len = sequence length, chunk_stride = block_len (no overlap)
const int64_t block_len = shape.empty() ? flat_width : shape.back();
const int64_t chunk_stride = block_len;
// Ensure URIs are absolute
const std::string abs_model_uri = EnsureAbsoluteUri( model_uri );
const std::string abs_input_uri = EnsureAbsoluteUri( input_uri );
// Derive output URI from input URI (replace extension with _output.raw)
std::string output_uri = abs_input_uri;
auto dot_pos = output_uri.rfind( '.' );
if ( dot_pos != std::string::npos )
{
output_uri = output_uri.substr( 0, dot_pos ) + "_output.raw";
}
else
{
output_uri += "_output.raw";
}
// -----------------------------------------------------------------------
// Build JSON matching the GNUS schema exactly
// -----------------------------------------------------------------------
nlohmann::json doc;
doc["name"] = "neo-swarm-inference";
doc["version"] = "1.0.0";
doc["gnus_spec_version"] = 1.0;
doc["description"] = "NeoSwarm inference job";
// inputs
nlohmann::json input_decl;
input_decl["name"] = "modelInput";
input_decl["source_uri_param"] = abs_input_uri;
input_decl["type"] = type_str;
input_decl["format"] = format_str;
input_decl["dimensions"] = {
{ "width", flat_width }, { "block_len", block_len }, { "chunk_stride", chunk_stride } };
doc["inputs"] = nlohmann::json::array( { input_decl } );
// outputs
nlohmann::json output_decl;
output_decl["name"] = "inferenceOutput";
output_decl["source_uri_param"] = output_uri;
output_decl["type"] = "tensor";
doc["outputs"] = nlohmann::json::array( { output_decl } );
// passes
nlohmann::json input_node;
input_node["name"] = "input";
input_node["type"] = "tensor";
input_node["source"] = "input:modelInput";
input_node["shape"] = shape_json;
nlohmann::json output_node;
output_node["name"] = "output";
output_node["type"] = "tensor";
output_node["target"] = "output:inferenceOutput";
output_node["shape"] = shape_json;
nlohmann::json model_config;
model_config["source_uri_param"] = abs_model_uri;
model_config["format"] = "MNN";
model_config["batch_size"] = 1;
model_config["input_nodes"] = nlohmann::json::array( { input_node } );
model_config["output_nodes"] = nlohmann::json::array( { output_node } );
nlohmann::json pass;
pass["name"] = "inference";
pass["type"] = "inference";
pass["description"] = "MNN inference pass";
pass["model"] = model_config;
doc["passes"] = nlohmann::json::array( { pass } );
return outcome::success( doc.dump() );
}
// -----------------------------------------------------------------------
// SubmitJob
// -----------------------------------------------------------------------
outcome::result<std::vector<uint8_t>> SGProcessingBridge::SubmitJob( const std::string& model_uri,
const std::string& input_uri,
sgns::InputFormat input_format,
const std::vector<int64_t>& shape,
std::shared_ptr<boost::asio::io_context> ioc )
{
BridgeLogger()->debug( "SubmitJob model={} format={} networkMode={}", model_uri,
InputFormatToFormatString( input_format ), static_cast<int>( m_cfg.m_networkMode ) );
auto json_res = BuildSchemaJson( model_uri, input_uri, input_format, shape );
if ( !json_res.has_value() )
{
return outcome::failure( json_res.error() );
}
if ( m_cfg.m_networkMode )
{
auto result = SubmitNetwork( json_res.value() );
if ( !result.has_value() )
{
// Auto-fallback to local MNN on network failure
// Auth failures (SignatureInvalid) are NOT silently swallowed
if ( result.error() == Error::SignatureInvalid || result.error() == Error::IdentityError )
{
BridgeLogger()->error( "Network dispatch auth failed — NOT falling back to local mode" );
return result;
}
BridgeLogger()->warn( "Network dispatch failed ({}), falling back to local mode",
result.error().message() );
return SubmitDirect( json_res.value(), ioc );
}
return result;
}
return SubmitDirect( json_res.value(), ioc );
}
// -----------------------------------------------------------------------
// SubmitDirect — Phase 1: call ProcessingManager locally
//
// Follows the exact pattern from processing_datatypes_test.cpp:
// 1. ProcessingManager::Create(json)
// 2. GetProcessingData() → get_passes()[0].get_model() → get_input_nodes()[0]
// 3. Process(ioc, chunkhashes, model_node)
// -----------------------------------------------------------------------
outcome::result<std::vector<uint8_t>> SGProcessingBridge::SubmitDirect(
const std::string& jsondata,
std::shared_ptr<boost::asio::io_context> ioc ) const
{
// Step 1: Create ProcessingManager from JSON
auto pm_result = sgns::sgprocessing::ProcessingManager::Create( jsondata );
if ( !pm_result )
{
BridgeLogger()->error( "ProcessingManager::Create failed (error={})", pm_result.error().message() );
return outcome::failure( Error::InferenceFailed );
}
auto pm = pm_result.value();
// Step 2: Extract ModelNode from the first pass's first input node
// This is the exact pattern from processing_datatypes_test.cpp
auto processing = pm->GetProcessingData();
const auto& passes = processing.get_passes();
if ( passes.empty() )
{
return outcome::failure( Error::InferenceFailed );
}
if ( !passes[0].get_model().has_value() )
{
return outcome::failure( Error::InferenceFailed );
}
const auto model_config = passes[0].get_model().value();
const auto input_nodes = model_config.get_input_nodes();
if ( input_nodes.empty() )
{
return outcome::failure( Error::InferenceFailed );
}
sgns::ModelNode model_node = input_nodes[0];
// Step 3: Run inference
std::vector<std::vector<uint8_t>> chunkhashes;
auto process_result = pm->Process( ioc, chunkhashes, model_node );
if ( !process_result )
{
BridgeLogger()->error( "ProcessingManager::Process failed (error={})", process_result.error().message() );
return outcome::failure( Error::InferenceFailed );
}
BridgeLogger()->debug( "Process() succeeded: {} bytes, {} chunk hashes", process_result.value().size(),
chunkhashes.size() );
return outcome::success( process_result.value() );
(void) jsondata;
(void) ioc;
BridgeLogger()->warn( "SGProcessingBridge: SGProcessingManager not compiled in — stub mode" );
return outcome::success( std::vector<uint8_t>{} );
}
// -----------------------------------------------------------------------
// SetClient
// -----------------------------------------------------------------------
void SGProcessingBridge::SetClient( network::SGClient* client ) noexcept
{
m_client = client;
BridgeLogger()->info( "SGClient set (m_networkMode={})", client ? "true" : "false" );
}
// -----------------------------------------------------------------------
// SubmitNetwork — Phase 2: dispatch via GeniusSDK
// -----------------------------------------------------------------------
outcome::result<std::vector<uint8_t>> SGProcessingBridge::SubmitNetwork( const std::string& jsondata ) const
{
if ( !m_client )
{
BridgeLogger()->error( "SubmitNetwork: SGClient not configured" );
return outcome::failure( Error::NetworkError );
}
BridgeLogger()->debug( "Dispatching job via GeniusSDK ({} bytes)", jsondata.size() );
return m_client->SubmitJob( jsondata );
}
} // namespace sgns::neoswarm::core
Updated on 2026-07-25 at 22:56:57 +0000