Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Source/Private/Core/N2CSerializer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -751,7 +751,7 @@ bool FN2CSerializer::ParseFlowsFromJson(const TSharedPtr<FJsonObject>& JsonObjec
{
if (DataFlow.Value->Type == EJson::String)
{
OutFlows.Data.Add(DataFlow.Key, DataFlow.Value->AsString());
OutFlows.Data.Add(FString(DataFlow.Key.ToView()), DataFlow.Value->AsString());
}
}

Expand Down
33 changes: 32 additions & 1 deletion Source/Private/LLM/Providers/N2CLMStudioResponseParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,35 @@ bool UN2CLMStudioResponseParser::ParseLLMResponse(
return false;
}

// Some reasoning models served through LM Studio return the final structured output
// in reasoning_content while leaving content empty.
if (MessageContent.TrimStartAndEnd().IsEmpty())
{
const TArray<TSharedPtr<FJsonValue>>* ChoicesArray = nullptr;
if (JsonObject->TryGetArrayField(TEXT("choices"), ChoicesArray) && ChoicesArray && ChoicesArray->Num() > 0)
{
const TSharedPtr<FJsonObject> ChoiceObject = (*ChoicesArray)[0]->AsObject();
if (ChoiceObject.IsValid())
{
const TSharedPtr<FJsonObject> MessageObject = ChoiceObject->GetObjectField(TEXT("message"));
if (MessageObject.IsValid())
{
FString ReasoningContent;
if (MessageObject->TryGetStringField(TEXT("reasoning_content"), ReasoningContent) &&
!ReasoningContent.TrimStartAndEnd().IsEmpty())
{
MessageContent = ReasoningContent;
FN2CLogger::Get().Log(
TEXT("Using LM Studio reasoning_content because message content was empty"),
EN2CLogSeverity::Info,
TEXT("LMStudioResponseParser")
);
}
}
}
}
}

// Extract usage information (OpenAI-compatible format)
const TSharedPtr<FJsonObject> UsageObject = JsonObject->GetObjectField(TEXT("usage"));
if (UsageObject.IsValid())
Expand Down Expand Up @@ -80,7 +109,9 @@ bool UN2CLMStudioResponseParser::ParseLLMResponse(
}

// Log model info if available
const TSharedPtr<FJsonObject> ModelInfoObject = JsonObject->GetObjectField(TEXT("model_info"));
const TSharedPtr<FJsonObject> ModelInfoObject = JsonObject->HasTypedField<EJson::Object>(TEXT("model_info"))
? JsonObject->GetObjectField(TEXT("model_info"))
: nullptr;
if (ModelInfoObject.IsValid())
{
FString Architecture, Quantization, Format;
Expand Down
19 changes: 15 additions & 4 deletions Source/Private/LLM/Providers/N2CLMStudioService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,15 +97,26 @@ FString UN2CLMStudioService::FormatRequestPayload(const FString& UserMessage, co

// Prepend user message text if configured
const UN2CSettings* Settings = GetDefault<UN2CSettings>();
if (Settings && !Settings->LMStudioPrependedModelCommand.IsEmpty())
if (Settings)
{
FinalUserMessage = Settings->LMStudioPrependedModelCommand + TEXT("\n\n") + FinalUserMessage;
PayloadBuilder->SetMaxTokens(Settings->LMStudioMaxTokens);

FN2CLogger::Get().Log(
FString::Printf(TEXT("Prepended model command text: %s"), *Settings->LMStudioPrependedModelCommand),
FString::Printf(TEXT("Using LM Studio max tokens: %d"), Settings->LMStudioMaxTokens),
EN2CLogSeverity::Debug,
TEXT("LMStudioService")
);

if (!Settings->LMStudioPrependedModelCommand.IsEmpty())
{
FinalUserMessage = Settings->LMStudioPrependedModelCommand + TEXT("\n\n") + FinalUserMessage;

FN2CLogger::Get().Log(
FString::Printf(TEXT("Prepended model command text: %s"), *Settings->LMStudioPrependedModelCommand),
EN2CLogSeverity::Debug,
TEXT("LMStudioService")
);
}
}

// Add messages - LM Studio supports system prompts
Expand Down
5 changes: 5 additions & 0 deletions Source/Public/Core/N2CSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,11 @@ class NODETOCODE_API UN2CSettings : public UDeveloperSettings
UPROPERTY(Config, EditAnywhere, BlueprintReadOnly, Category = "Node to Code | LLM Services | LM Studio",
meta=(DisplayName="Server Endpoint"))
FString LMStudioEndpoint = "http://localhost:1234";

/** LM Studio maximum output tokens */
UPROPERTY(Config, EditAnywhere, BlueprintReadOnly, Category = "Node to Code | LLM Services | LM Studio",
meta=(DisplayName="Max Tokens", ClampMin="1", UIMin="1"))
int32 LMStudioMaxTokens = 32768;

/** LM Studio Prepended Model Command - Text to prepend to user messages (e.g., '/no_think' to disable thinking for reasoning models, or other model-specific commands). This text will appear at the start of each user message. */
UPROPERTY(Config, EditAnywhere, BlueprintReadOnly, Category = "Node to Code | LLM Services | LM Studio",
Expand Down