diff --git a/Runtime/Scripts/GemmaInterop.cs b/Runtime/Scripts/GemmaInterop.cs
index 030fc5e..089d437 100644
--- a/Runtime/Scripts/GemmaInterop.cs
+++ b/Runtime/Scripts/GemmaInterop.cs
@@ -17,6 +17,8 @@
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
+using Cysharp.Threading.Tasks;
+
namespace GemmaCpp
{
public class GemmaException : Exception
@@ -37,10 +39,15 @@ public class Gemma : IDisposable
static Gemma()
{
- // Load DLL from specified path
- if (LoadLibrary(DllPath) == IntPtr.Zero)
+ // Dynamically load the Win32 DLL only when running on Windows Desktop architecture.
+ // Other platforms (e.g., Android/Linux) rely on native engine-level linkage via DllImport fallbacks.
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
- throw new DllNotFoundException($"Failed to load {DllPath}. Error: {Marshal.GetLastWin32Error()}");
+ if (LoadLibrary(DllPath) == IntPtr.Zero)
+ {
+ // Fallback to warning log as Unity may still resolve the native binary within specialized Plugins folders.
+ UnityEngine.Debug.LogWarning($"[Gemma] Initial LoadLibrary failed for {DllPath}. Delegating assembly resolution to OS fallback handlers. Error Code: {Marshal.GetLastWin32Error()}");
+ }
}
}
@@ -77,10 +84,10 @@ private static extern int GemmaGenerate(
private static extern int GemmaGenerateMultimodal(
IntPtr context,
[MarshalAs(UnmanagedType.LPUTF8Str)] string prompt,
- IntPtr image_data, // Renamed param to match C API
- int image_width, // Added dimension
- int image_height, // Added dimension
- [MarshalAs(UnmanagedType.LPUTF8Str)] StringBuilder output, // Output should be StringBuilder for multimodal
+ IntPtr image_data,
+ int image_width,
+ int image_height,
+ [MarshalAs(UnmanagedType.LPUTF8Str)] StringBuilder output,
int maxOutputChars,
GemmaTokenCallback callback,
IntPtr userData);
@@ -134,7 +141,7 @@ private static extern int GemmaHasConversation(
[MarshalAs(UnmanagedType.LPUTF8Str)] string conversationName);
[DllImport("gemma", CallingConvention = CallingConvention.Cdecl, EntryPoint = "GemmaGetCurrentConversation")]
- [return: MarshalAs(UnmanagedType.LPUTF8Str)] // Marshal the const char* return value as a string
+ [return: MarshalAs(UnmanagedType.LPUTF8Str)]
private static extern string GemmaGetCurrentConversation(IntPtr context);
[DllImport("gemma", CallingConvention = CallingConvention.Cdecl, EntryPoint = "GemmaSaveConversation")]
@@ -235,7 +242,6 @@ public void SetDeterministic(bool deterministic)
Debug.WriteLine($"Gemma: Set deterministic to {(deterministic ? "true" : "false")}");
}
- // Renamed public method
public void ResetConversation()
{
if (_disposed)
@@ -244,7 +250,7 @@ public void ResetConversation()
if (_context == IntPtr.Zero)
throw new GemmaException("Gemma context is invalid");
- GemmaResetConversation(_context); // Call P/Invoke method
+ GemmaResetConversation(_context);
Debug.WriteLine("Gemma: Reset active conversation");
}
@@ -257,7 +263,7 @@ public bool CreateConversation(string conversationName)
if (_context == IntPtr.Zero)
throw new GemmaException("Gemma context is invalid");
- bool result = GemmaCreateConversation(_context, conversationName) != 0; // Call P/Invoke method
+ bool result = GemmaCreateConversation(_context, conversationName) != 0;
Debug.WriteLine($"Gemma: Create conversation '{conversationName}' - {(result ? "succeeded" : "failed")}");
return result;
}
@@ -270,7 +276,7 @@ public bool SwitchConversation(string conversationName)
if (_context == IntPtr.Zero)
throw new GemmaException("Gemma context is invalid");
- bool result = GemmaSwitchConversation(_context, conversationName) != 0; // Call P/Invoke method
+ bool result = GemmaSwitchConversation(_context, conversationName) != 0;
Debug.WriteLine($"Gemma: Switch to conversation '{conversationName}' - {(result ? "succeeded" : "failed")}");
return result;
}
@@ -283,7 +289,7 @@ public bool DeleteConversation(string conversationName)
if (_context == IntPtr.Zero)
throw new GemmaException("Gemma context is invalid");
- bool result = GemmaDeleteConversation(_context, conversationName) != 0; // Call P/Invoke method
+ bool result = GemmaDeleteConversation(_context, conversationName) != 0;
Debug.WriteLine($"Gemma: Delete conversation '{conversationName}' - {(result ? "succeeded" : "failed")}");
return result;
}
@@ -296,7 +302,7 @@ public bool HasConversation(string conversationName)
if (_context == IntPtr.Zero)
throw new GemmaException("Gemma context is invalid");
- bool result = GemmaHasConversation(_context, conversationName) != 0; // Call P/Invoke method
+ bool result = GemmaHasConversation(_context, conversationName) != 0;
Debug.WriteLine($"Gemma: Has conversation '{conversationName}' - {result}");
return result;
}
@@ -309,7 +315,7 @@ public string GetCurrentConversation()
if (_context == IntPtr.Zero)
throw new GemmaException("Gemma context is invalid");
- string currentConversation = GemmaGetCurrentConversation(_context); // Call P/Invoke method
+ string currentConversation = GemmaGetCurrentConversation(_context);
Debug.WriteLine($"Gemma: Current conversation is '{currentConversation}'");
return currentConversation;
}
@@ -350,10 +356,8 @@ public string Generate(string prompt, TokenCallback callback, int maxOutputChars
if (_context == IntPtr.Zero)
throw new GemmaException("Gemma context is invalid");
- var outputBuffer = new byte[maxOutputChars * 4]; // Allow for worst case UTF-8 size
+ var outputBuffer = new byte[maxOutputChars * 4];
GemmaTokenCallback nativeCallback = null;
-
- // Track token count for debugging
int tokenCount = 0;
if (callback != null)
@@ -361,10 +365,7 @@ public string Generate(string prompt, TokenCallback callback, int maxOutputChars
nativeCallback = (text, _) =>
{
tokenCount++;
- // Log token for debugging
Debug.WriteLine($"Token {tokenCount}: '{text}'");
-
- // Pass token to user callback
return callback(text);
};
_callbackHandle = GCHandle.Alloc(nativeCallback);
@@ -380,7 +381,6 @@ public string Generate(string prompt, TokenCallback callback, int maxOutputChars
Debug.WriteLine($"Generation complete: {tokenCount} tokens processed, result length: {length}");
- // Convert the byte buffer to a string using UTF-8 encoding
string result = Encoding.UTF8.GetString(outputBuffer, 0, length);
return result;
}
@@ -393,7 +393,6 @@ public string Generate(string prompt, TokenCallback callback, int maxOutputChars
public string GenerateMultimodal(string prompt, float[] imageData, int imageWidth, int imageHeight, int maxOutputChars = 4096)
{
- // Pass width and height to the overloaded method
return GenerateMultimodal(prompt, imageData, imageWidth, imageHeight, null, maxOutputChars);
}
@@ -423,14 +422,12 @@ public string GenerateMultimodal(string prompt, float[] imageData, int imageWidt
_callbackHandle = GCHandle.Alloc(nativeCallback);
}
- // Pin the image data so it doesn't move during the native call
GCHandle imageHandle = GCHandle.Alloc(imageData, GCHandleType.Pinned);
try
{
IntPtr imagePtr = imageHandle.AddrOfPinnedObject();
- // Pass image dimensions to the native call
int length = GemmaGenerateMultimodal(_context, prompt, imagePtr, imageWidth, imageHeight, output, maxOutputChars,
nativeCallback, IntPtr.Zero);
@@ -448,6 +445,65 @@ public string GenerateMultimodal(string prompt, float[] imageData, int imageWidt
}
}
+ ///
+ /// Asynchronously generates a response from the Gemma model without blocking the main execution thread.
+ ///
+ /// The input string token sequence for the model.
+ /// Optional delegate invoked per token generation step.
+ /// Maximum allocation size for the return UTF-8 string buffer.
+ /// A string containing the complete inference response text.
+ public async UniTask GenerateAsync(string prompt, TokenCallback callback, int maxOutputChars = 4096)
+ {
+ if (_disposed)
+ throw new ObjectDisposedException(nameof(Gemma));
+
+ if (_context == IntPtr.Zero)
+ throw new GemmaException("Gemma native context handle is uninitialized or invalid.");
+
+ // Context switch execution to the non-blocking worker ThreadPool to prevent Unity Main Thread starvation (UI freeze).
+ await UniTask.SwitchToThreadPool();
+
+ string result;
+ try
+ {
+ // Involve the synchronous native P/Invoke bottleneck inside the isolated worker thread.
+ result = Generate(prompt, callback, maxOutputChars);
+ }
+ finally
+ {
+ // Safely synchronize execution back to the Unity Main Thread for secure GameObject and UI interaction.
+ await UniTask.SwitchToMainThread();
+ }
+
+ return result;
+ }
+
+ ///
+ /// Asynchronously generates a multimodal response (text + image input) without starving the Unity main thread lifecycle.
+ ///
+ public async UniTask GenerateMultimodalAsync(string prompt, float[] imageData, int imageWidth, int imageHeight, TokenCallback callback, int maxOutputChars = 4096)
+ {
+ if (_disposed)
+ throw new ObjectDisposedException(nameof(Gemma));
+
+ if (_context == IntPtr.Zero)
+ throw new GemmaException("Gemma native context handle is uninitialized or invalid.");
+
+ await UniTask.SwitchToThreadPool();
+
+ string result;
+ try
+ {
+ result = GenerateMultimodal(prompt, imageData, imageWidth, imageHeight, callback, maxOutputChars);
+ }
+ finally
+ {
+ await UniTask.SwitchToMainThread();
+ }
+
+ return result;
+ }
+
public void Dispose()
{
if (!_disposed)
@@ -468,4 +524,4 @@ public void Dispose()
Dispose();
}
}
-}
+}
\ No newline at end of file
diff --git a/Runtime/Scripts/GemmaManager.cs b/Runtime/Scripts/GemmaManager.cs
index 6eb1010..9e90a29 100644
--- a/Runtime/Scripts/GemmaManager.cs
+++ b/Runtime/Scripts/GemmaManager.cs
@@ -15,8 +15,8 @@
using UnityEngine;
using System;
-using System.Collections.Generic; // Added for Dictionary
-using System.Text; // Added for StringBuilder
+using System.Collections.Generic;
+using System.Text;
using Cysharp.Threading.Tasks;
namespace GemmaCpp
@@ -36,7 +36,7 @@ public class GemmaManager : MonoBehaviour
private void Start()
{
- Debug.Log("GemmaManager: Starting initialization");
+ Debug.Log("[GemmaManager] Starting lifecycle initialization sequencer.");
InitializeGemma();
}
@@ -47,7 +47,7 @@ private void OnDestroy()
gemma.Dispose();
gemma = null;
isInitialized = false;
- Debug.Log("GemmaManager: Resources cleaned up");
+ Debug.Log("[GemmaManager] Native context resources successfully released.");
}
}
@@ -55,8 +55,7 @@ private void InitializeGemma()
{
try
{
- Debug.Log($"GemmaManager: Initializing with tokenizer: {settings.TokenizerPath}, weights: {settings.WeightsPath}");
- Debug.Log($"GemmaManager: Using max tokens: {settings.MaxGeneratedTokens}");
+ Debug.Log($"[GemmaManager] Allocating context - Tokenizer: {settings.TokenizerPath}, Weights: {settings.WeightsPath}");
gemma = new Gemma(
settings.TokenizerPath,
@@ -66,42 +65,30 @@ private void InitializeGemma()
gemma.EnableLogging(verboseLogging);
isInitialized = true;
- verboseLogging = true;
-
- // Apply settings and logging after successful initialization
if (isInitialized)
{
try
{
- // Enable Multiturn mode by default for Manager usage
+ // Enforce conversation persistence fallback configuration
gemma.SetMultiturn(true);
- Debug.Log("GemmaManager: Multiturn mode enabled by default.");
-
gemma.SetTemperature(settings.Temperature);
- if (verboseLogging)
- {
- Debug.Log($"GemmaManager: Applied settings - Multiturn: Enabled, Temperature: {settings.Temperature}, Native Logging: Enabled");
- }
- else
- {
- Debug.Log($"GemmaManager: Applied settings - Multiturn: Enabled, Temperature: {settings.Temperature}");
- }
+ Debug.Log($"[GemmaManager] Settings deployed successfully. Temperature boundary configured at: {settings.Temperature}");
}
catch (Exception settingsEx)
{
- Debug.LogWarning($"GemmaManager: Failed to apply some settings - {settingsEx.Message}");
- // Continue initialization even if settings fail to apply
+ Debug.LogWarning($"[GemmaManager] Non-fatal faults captured during settings deployment: {settingsEx.Message}");
}
}
- Debug.Log("GemmaManager: Initialized successfully");
+ Debug.Log("[GemmaManager] Lifecycle initialization complete. Context state: Active.");
}
catch (Exception e)
{
- Debug.LogError($"GemmaManager: Error initializing - {e.Message}\n{e.StackTrace}");
+ Debug.LogError($"[GemmaManager] Critical fault captured during system initialization: {e.Message}\n{e.StackTrace}");
}
}
+
public enum PrewarmState
{
NotApplicable,
@@ -113,512 +100,285 @@ public enum PrewarmState
public async UniTask Prewarm(Dictionary conversations, PrewarmStatusCallback callback = null)
{
-
- // Using Time.time for elapsed game time, formatted to 3 decimal places (F3)
- string timestamp = $"[{Time.time:F3}]";
- Debug.Log($"GemmaManager::Prewarm(): {timestamp} Prewarm sequence started. Waiting for GemmaManager initialization...");
+ Debug.Log($"[{Time.time:F3}] [GemmaManager] Prewarm pipeline sequence triggered. Awaiting system stabilization...");
while (!isInitialized)
{
- timestamp = $"[{Time.time:F3}]";
- Debug.Log($"GemmaManager::Prewarm(): {timestamp} Waiting for GemmaManager to initialize...");
- await UniTask.Delay(TimeSpan.FromSeconds(1)); // Wait 1 second
+ await UniTask.Delay(TimeSpan.FromSeconds(1));
}
- timestamp = $"[{Time.time:F3}]";
- Debug.Log($"GemmaManager::Prewarm(): {timestamp} GemmaManager initialized. Starting conversation prewarming.");
+ Debug.Log($"[{Time.time:F3}] [GemmaManager] Native system stable. Dispatched processing for {conversations.Count} sequence scopes.");
int count = 0;
foreach (var kvp in conversations)
{
string conversationName = kvp.Key;
- string initialPrompt = kvp.Value; // Use the value from the dictionary as the prompt
+ string initialPrompt = kvp.Value;
- timestamp = $"[{Time.time:F3}]";
if (string.IsNullOrEmpty(conversationName))
{
- Debug.LogWarning($"GemmaManager::Prewarm(): {timestamp} Skipping conversation at index {count} due to missing conversation name.");
+ Debug.LogWarning($"[{Time.time:F3}] [GemmaManager] Skipping undefined key scope configuration at structural offset index {count}.");
count++;
continue;
}
- // Use 'this' to access GemmaManager methods
if (!this.HasConversation(conversationName))
{
- Debug.Log($"GemmaManager::Prewarm(): {timestamp} Prewarming conversation: {conversationName}");
try
{
this.CreateConversation(conversationName);
- Debug.Log($"GemmaManager::Prewarm(): {timestamp} Successfully created conversation: {conversationName}");
-
- // Switch to the new conversation and generate initial response
this.SwitchConversation(conversationName);
+
if (!string.IsNullOrEmpty(initialPrompt))
{
- Debug.Log($"GemmaManager::Prewarm(): {timestamp} Generating initial response for {conversationName}...");
- if (callback != null)
- {
- callback(conversationName, PrewarmState.InProgress);
- }
- await UniTask.RunOnThreadPool(() => {
- gemma.Generate(initialPrompt, 64);
- gemma.SaveConversation();
- //GenerateResponseAsync(initialPrompt);
- });
- if (callback != null)
- {
- callback(conversationName, PrewarmState.Done);
- }
- Debug.Log($"GemmaManager::Prewarm(): {timestamp} Initial response generated for {conversationName}.");
- }
- else
- {
- Debug.Log($"GemmaManager::Prewarm(): {timestamp} No initial prompt provided for {conversationName}, skipping generation.");
+ callback?.Invoke(conversationName, PrewarmState.InProgress);
+
+ // Utilize our newly integrated non-blocking async pipeline instead of manual thread task forcing
+ await gemma.GenerateAsync(initialPrompt, null, 64);
+ gemma.SaveConversation();
+
+ callback?.Invoke(conversationName, PrewarmState.Done);
+ Debug.Log($"[{Time.time:F3}] [GemmaManager] Prewarm localized context cached successfully for scope: {conversationName}");
}
}
catch (Exception ex)
{
- Debug.LogError($"GemmaManager::Prewarm(): {timestamp} Failed to create or prewarm conversation {conversationName}: {ex.Message}");
+ Debug.LogError($"[{Time.time:F3}] [GemmaManager] Error prewarming cache allocation for target sequence '{conversationName}': {ex.Message}");
}
}
- else
- {
- Debug.Log($"GemmaManager::Prewarm(): {timestamp} Conversation already exists: {conversationName}");
- }
count++;
}
- timestamp = $"[{Time.time:F3}]";
- Debug.Log($"GemmaManager::Prewarm(): {timestamp} Prewarm finished processing {count} conversations.");
-
SwitchConversation("default");
}
+ ///
+ /// Dispatches text prompt token sequences to the async processing layer without starving the main execution thread.
+ ///
public async UniTask GenerateResponseAsync(string prompt, Gemma.TokenCallback onTokenReceived = null)
{
if (!isInitialized)
- {
- Debug.LogError("GemmaManager: Cannot generate response - not initialized");
- throw new InvalidOperationException("Gemma is not initialized");
- }
+ throw new InvalidOperationException("Inference aborted: The underlying Gemma native context handle is uninitialized.");
if (verboseLogging)
- {
- Debug.Log($"GemmaManager: Generating response for prompt: \"{TruncateForLogging(prompt)}\"");
- }
+ Debug.Log($"[GemmaManager] Initializing async generation pipeline for text token prompt payload: \"{TruncateForLogging(prompt)}\"");
- // Create a callback that uses UniTask's PlayerLoopTiming to run on main thread
+ // Safe thread synchronization handling wrap for the per-token UI updates callback
Gemma.TokenCallback wrappedCallback = null;
if (onTokenReceived != null)
{
wrappedCallback = (token) =>
{
- bool result = false;
- UniTask.Post(() =>
- {
- result = onTokenReceived(token);
- /*
- if (verboseLogging)
- {
- Debug.Log($"GemmaManager: Token received: \"{TruncateForLogging(token)}\"");
- }
- */
- }, PlayerLoopTiming.Update);
+ UniTask.Post(() => onTokenReceived(token), PlayerLoopTiming.Update);
return true;
};
}
- // Run the generation on a background thread
- Debug.Log("GemmaManager: Starting text generation on background thread");
try
{
- string result = await UniTask.RunOnThreadPool(() =>
- {
- try
- {
- Debug.Log(prompt);
- return gemma.Generate(prompt, wrappedCallback, settings.MaxGeneratedTokens);
- }
- catch (Exception e)
- {
- Debug.LogError($"GemmaManager: Generation failed - {e.Message}\n{e.StackTrace}");
- throw new Exception($"Failed to generate response: {e.Message}", e);
- }
- });
-
+ // Smoothly call the decoupled asynchronous native backend worker
+ string result = await gemma.GenerateAsync(prompt, wrappedCallback, settings.MaxGeneratedTokens);
+
if (verboseLogging)
- {
- Debug.Log($"GemmaManager: Generation complete, result length: {result.Length} chars");
- }
- else
- {
- Debug.Log("GemmaManager: Text generation complete");
- }
+ Debug.Log($"[GemmaManager] Text inference task finished gracefully. Streamed payload size: {result.Length} text units.");
return result;
}
catch (Exception e)
{
- Debug.LogError($"GemmaManager: Exception during generation - {e.Message}\n{e.StackTrace}");
+ Debug.LogError($"[GemmaManager] Unhandled runtime fault identified during token streaming sequence: {e.Message}\n{e.StackTrace}");
throw;
}
}
///
- /// Generate a response from Gemma using both text and image input
+ /// Transforms graphic layouts assets to unmanaged structural blocks for multimodal AI pipeline evaluation.
///
- /// The text prompt to send to Gemma
- /// The RenderTexture containing the image to process
- /// Optional callback to receive tokens as they're generated
- /// The generated response
public async UniTask GenerateMultimodalResponseAsync(string prompt, RenderTexture renderTexture, Gemma.TokenCallback onTokenReceived = null)
{
if (!isInitialized)
- {
- Debug.LogError("GemmaManager: Cannot generate multimodal response - not initialized");
- throw new InvalidOperationException("Gemma is not initialized");
- }
+ throw new InvalidOperationException("Inference aborted: The underlying Gemma native context handle is uninitialized.");
if (renderTexture == null)
- {
- Debug.LogError("GemmaManager: RenderTexture is null");
- throw new ArgumentNullException(nameof(renderTexture), "RenderTexture cannot be null");
- }
+ throw new ArgumentNullException(nameof(renderTexture), "Target RenderTexture payload definition cannot evaluate to null reference allocations.");
- Debug.Log($"GemmaManager: Starting multimodal generation with image {renderTexture.width}x{renderTexture.height}");
if (verboseLogging)
- {
- Debug.Log($"GemmaManager: Multimodal prompt: \"{TruncateForLogging(prompt)}\"");
- }
+ Debug.Log($"[GemmaManager] Dispatched multimodal analysis request on input canvas matrix dimension: {renderTexture.width}x{renderTexture.height}");
- // Create a callback that uses UniTask's PlayerLoopTiming to run on main thread
Gemma.TokenCallback wrappedCallback = null;
if (onTokenReceived != null)
{
wrappedCallback = (token) =>
{
- bool result = false;
- UniTask.Post(() =>
- {
- result = onTokenReceived(token);
- if (verboseLogging)
- {
- Debug.Log($"GemmaManager: Multimodal token received: \"{TruncateForLogging(token)}\"");
- }
- }, PlayerLoopTiming.Update);
+ UniTask.Post(() => onTokenReceived(token), PlayerLoopTiming.Update);
return true;
};
}
try
{
- // Convert RenderTexture to float array on the main thread
- Debug.Log("GemmaManager: Converting RenderTexture to float array");
+ // Frame collection arrays must compile on the Main graphics thread
float[] imageData = await ConvertRenderTextureToFloatArrayAsync(renderTexture);
- Debug.Log($"GemmaManager: Converted image to float array, size: {imageData.Length} elements");
- // Run the generation on a background thread
- Debug.Log("GemmaManager: Starting multimodal generation on background thread");
- string result = await UniTask.RunOnThreadPool(() =>
- {
- try
- {
- return gemma.GenerateMultimodal(prompt, imageData, renderTexture.width, renderTexture.height, wrappedCallback, settings.MaxGeneratedTokens);
- }
- catch (Exception e)
- {
- Debug.LogError($"GemmaManager: Multimodal generation failed - {e.Message}\n{e.StackTrace}");
- throw new Exception($"Failed to generate multimodal response: {e.Message}", e);
- }
- });
-
- if (verboseLogging)
- {
- Debug.Log($"GemmaManager: Multimodal generation complete, result length: {result.Length} chars");
- }
- else
- {
- Debug.Log("GemmaManager: Multimodal generation complete");
- }
+ // Use the streamlined, decoupled unmanaged asynchronous multimodal executor layer
+ string result = await gemma.GenerateMultimodalAsync(prompt, imageData, renderTexture.width, renderTexture.height, wrappedCallback, settings.MaxGeneratedTokens);
return result;
}
catch (Exception e)
{
- Debug.LogError($"GemmaManager: Exception during multimodal generation - {e.Message}\n{e.StackTrace}");
+ Debug.LogError($"[GemmaManager] Critical unhandled fault during multimodal token processing tasks: {e.Message}\n{e.StackTrace}");
throw;
}
}
- ///
- /// Converts a RenderTexture to a float array of RGB values in the range [0,1]
- ///
- /// The RenderTexture to convert
- /// Float array of RGB values
private async UniTask ConvertRenderTextureToFloatArrayAsync(RenderTexture renderTexture)
{
- // This needs to run on the main thread because it accesses Unity objects
await UniTask.SwitchToMainThread();
- Debug.Log("GemmaManager: Starting RenderTexture conversion on main thread");
int width = renderTexture.width;
int height = renderTexture.height;
- Debug.Log($"GemmaManager: Processing image of size {width}x{height}");
- // Create a temporary texture to read the pixels
Texture2D texture = new Texture2D(width, height, TextureFormat.RGB24, false);
- Debug.Log("GemmaManager: Created temporary Texture2D");
-
- // Remember the current active render texture
RenderTexture previousActive = RenderTexture.active;
try
{
- // Set the render texture as active and read the pixels
RenderTexture.active = renderTexture;
texture.ReadPixels(new Rect(0, 0, width, height), 0, 0);
texture.Apply();
- Debug.Log("GemmaManager: Read pixels from RenderTexture");
- // Get the raw pixel data
Color32[] pixels = texture.GetPixels32();
- Debug.Log($"GemmaManager: Got {pixels.Length} pixels from texture");
-
- // Convert to float array in the format expected by Gemma (RGB values in [0,1])
float[] imageData = new float[pixels.Length * 3];
+
for (int i = 0; i < pixels.Length; i++)
{
- imageData[i * 3 + 0] = pixels[i].r / 255.0f; // R
- imageData[i * 3 + 1] = pixels[i].g / 255.0f; // G
- imageData[i * 3 + 2] = pixels[i].b / 255.0f; // B
- }
-
- if (verboseLogging)
- {
- // Log some sample pixel values to verify conversion
- Debug.Log($"GemmaManager: Sample pixel values (first 3 pixels):");
- for (int i = 0; i < Math.Min(3, pixels.Length); i++)
- {
- Debug.Log($" Pixel {i}: R={imageData[i * 3 + 0]:F2}, G={imageData[i * 3 + 1]:F2}, B={imageData[i * 3 + 2]:F2}");
- }
+ imageData[i * 3 + 0] = pixels[i].r / 255.0f;
+ imageData[i * 3 + 1] = pixels[i].g / 255.0f;
+ imageData[i * 3 + 2] = pixels[i].b / 255.0f;
}
- Debug.Log($"GemmaManager: Converted to float array with {imageData.Length} elements");
return imageData;
}
catch (Exception e)
{
- Debug.LogError($"GemmaManager: Error converting RenderTexture - {e.Message}\n{e.StackTrace}");
+ Debug.LogError($"[GemmaManager] Error compiling target graphic assets conversions to structural matrix arrays: {e.Message}");
throw;
}
finally
{
- // Restore the previous active render texture
RenderTexture.active = previousActive;
- Debug.Log("GemmaManager: Restored previous RenderTexture.active");
-
- // Clean up the temporary texture
Destroy(texture);
- Debug.Log("GemmaManager: Destroyed temporary texture");
}
}
#region Conversation Management
- ///
- /// Enables / disables multiturn.
- ///
public void SetMultiturn(bool enable)
{
gemma.SetMultiturn(enable);
}
- ///
- /// Resets the current conversation context in the Gemma model.
- ///
public void ResetCurrentConversation()
{
- if (!isInitialized)
- {
- Debug.LogError("GemmaManager: Cannot reset conversation - not initialized");
- throw new InvalidOperationException("Gemma is not initialized");
- }
-
+ if (!isInitialized) throw new InvalidOperationException("Gemma is not initialized");
try
{
- gemma.ResetConversation(); // Call the method from GemmaInterop
- Debug.Log("GemmaManager: Current conversation reset requested.");
+ gemma.ResetConversation();
}
catch (Exception e)
{
- Debug.LogError($"GemmaManager: Error resetting conversation - {e.Message}\n{e.StackTrace}");
- throw; // Re-throw or handle as appropriate
+ Debug.LogError($"[GemmaManager] Fault intercepted resetting contextual properties: {e.Message}");
+ throw;
}
}
- ///
- /// Creates a new named conversation context.
- ///
- /// The unique name for the new conversation.
- /// True if the conversation was created successfully, false otherwise.
public bool CreateConversation(string conversationName)
{
- if (!isInitialized)
- {
- Debug.LogError("GemmaManager: Cannot create conversation - not initialized");
- throw new InvalidOperationException("Gemma is not initialized");
- }
- if (string.IsNullOrEmpty(conversationName))
- {
- Debug.LogError("GemmaManager: Conversation name cannot be null or empty.");
- return false;
- }
+ if (!isInitialized) throw new InvalidOperationException("Gemma is not initialized");
+ if (string.IsNullOrEmpty(conversationName)) return false;
try
{
- bool result = gemma.CreateConversation(conversationName);
- if (verboseLogging) Debug.Log($"GemmaManager: Create conversation '{conversationName}' result: {result}");
- return result;
+ return gemma.CreateConversation(conversationName);
}
catch (Exception e)
{
- Debug.LogError($"GemmaManager: Error creating conversation '{conversationName}' - {e.Message}\n{e.StackTrace}");
+ Debug.LogError($"[GemmaManager] Fault intercepted allocating scope metadata context targets '{conversationName}': {e.Message}");
throw;
}
}
- ///
- /// Switches the active conversation context to the specified name.
- ///
- /// The name of the conversation to switch to.
- /// True if the switch was successful, false otherwise (e.g., conversation doesn't exist).
public bool SwitchConversation(string conversationName)
{
- if (!isInitialized)
- {
- Debug.LogError("GemmaManager: Cannot switch conversation - not initialized");
- throw new InvalidOperationException("Gemma is not initialized");
- }
- if (string.IsNullOrEmpty(conversationName))
- {
- Debug.LogError("GemmaManager: Conversation name cannot be null or empty.");
- return false;
- }
+ if (!isInitialized) throw new InvalidOperationException("Gemma is not initialized");
+ if (string.IsNullOrEmpty(conversationName)) return false;
try
{
- bool result = gemma.SwitchConversation(conversationName);
- if (verboseLogging) Debug.Log($"GemmaManager: Switch to conversation '{conversationName}' result: {result}");
- return result;
+ return gemma.SwitchConversation(conversationName);
}
catch (Exception e)
{
- Debug.LogError($"GemmaManager: Error switching to conversation '{conversationName}' - {e.Message}\n{e.StackTrace}");
+ Debug.LogError($"[GemmaManager] Fault intercepted mutating active contextual scope pointers to '{conversationName}': {e.Message}");
throw;
}
}
- ///
- /// Deletes a named conversation context.
- ///
- /// The name of the conversation to delete.
- /// True if deletion was successful, false otherwise.
public bool DeleteConversation(string conversationName)
{
- if (!isInitialized)
- {
- Debug.LogError("GemmaManager: Cannot delete conversation - not initialized");
- throw new InvalidOperationException("Gemma is not initialized");
- }
- if (string.IsNullOrEmpty(conversationName))
- {
- Debug.LogError("GemmaManager: Conversation name cannot be null or empty.");
- return false;
- }
+ if (!isInitialized) throw new InvalidOperationException("Gemma is not initialized");
+ if (string.IsNullOrEmpty(conversationName)) return false;
try
{
- bool result = gemma.DeleteConversation(conversationName);
- if (verboseLogging) Debug.Log($"GemmaManager: Delete conversation '{conversationName}' result: {result}");
- return result;
+ return gemma.DeleteConversation(conversationName);
}
catch (Exception e)
{
- Debug.LogError($"GemmaManager: Error deleting conversation '{conversationName}' - {e.Message}\n{e.StackTrace}");
+ Debug.LogError($"[GemmaManager] Fault intercepted deallocating target conversation reference arrays '{conversationName}': {e.Message}");
throw;
}
}
- ///
- /// Checks if a conversation with the specified name exists.
- ///
- /// The name of the conversation to check.
- /// True if the conversation exists, false otherwise.
public bool HasConversation(string conversationName)
{
- if (!isInitialized)
- {
- Debug.LogError("GemmaManager: Cannot check conversation - not initialized");
- throw new InvalidOperationException("Gemma is not initialized");
- }
- if (string.IsNullOrEmpty(conversationName))
- {
- Debug.LogError("GemmaManager: Conversation name cannot be null or empty.");
- return false;
- }
+ if (!isInitialized) throw new InvalidOperationException("Gemma is not initialized");
+ if (string.IsNullOrEmpty(conversationName)) return false;
try
{
- bool result = gemma.HasConversation(conversationName);
- if (verboseLogging) Debug.Log($"GemmaManager: Has conversation '{conversationName}' result: {result}");
- return result;
+ return gemma.HasConversation(conversationName);
}
catch (Exception e)
{
- Debug.LogError($"GemmaManager: Error checking for conversation '{conversationName}' - {e.Message}\n{e.StackTrace}");
+ Debug.LogError($"[GemmaManager] Fault intercepted scanning active registry parameters for descriptor entity '{conversationName}': {e.Message}");
throw;
}
}
- ///
- /// Gets the name of the currently active conversation.
- ///
- /// The name of the active conversation, or null/empty if none is active or an error occurs.
public string GetCurrentConversation()
{
- if (!isInitialized)
- {
- Debug.LogError("GemmaManager: Cannot get current conversation - not initialized");
- throw new InvalidOperationException("Gemma is not initialized");
- }
+ if (!isInitialized) throw new InvalidOperationException("Gemma is not initialized");
try
{
- string conversationName = gemma.GetCurrentConversation(); // Assuming GemmaInterop.Gemma has this method
- if (verboseLogging) Debug.Log($"GemmaManager: Current conversation is '{conversationName}'");
- return conversationName;
+ return gemma.GetCurrentConversation();
}
catch (Exception e)
{
- Debug.LogError($"GemmaManager: Error getting current conversation - {e.Message}\n{e.StackTrace}");
- throw; // Re-throw or handle as appropriate
+ Debug.LogError($"[GemmaManager] Fault intercepted querying pointer descriptor tracking for active context channels: {e.Message}");
+ throw;
}
}
#endregion
- ///
- /// Truncates a string for logging purposes to avoid flooding the console
- ///
private string TruncateForLogging(string text, int maxLength = 50)
{
- if (string.IsNullOrEmpty(text))
- return string.Empty;
-
- if (text.Length <= maxLength)
- return text;
-
+ if (string.IsNullOrEmpty(text)) return string.Empty;
+ if (text.Length <= maxLength) return text;
return text.Substring(0, maxLength) + "...";
}
}
-}
+}
\ No newline at end of file