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
19 changes: 10 additions & 9 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,15 @@
<PackageVersion Include="Azure.Messaging.EventGrid" Version="5.0.0" />
<PackageVersion Include="Azure.Search.Documents" Version="11.7.0" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="10.0.3" />
<PackageVersion Include="Microsoft.Extensions.AI" Version="10.4.0" />
<PackageVersion Include="Microsoft.Extensions.AI.Abstractions" Version="10.4.0" />
<PackageVersion Include="Microsoft.Extensions.AI.OpenAI" Version="10.4.0" />
<PackageVersion Include="Microsoft.Extensions.AI.AzureAIInference" Version="10.0.0-preview.1.25559.3" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.5" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Physical" Version="10.0.5" />
<PackageVersion Include="Microsoft.Extensions.Localization.Abstractions" Version="10.0.5" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.5" />
<PackageVersion Include="Microsoft.Extensions.AI" Version="10.4.0" />
<PackageVersion Include="Microsoft.Extensions.AI.Abstractions" Version="10.4.0" />
<PackageVersion Include="Microsoft.Extensions.AI.OpenAI" Version="10.4.0" />
<PackageVersion Include="Microsoft.Extensions.AI.AzureAIInference" Version="10.0.0-preview.1.25559.3" />
<PackageVersion Include="Microsoft.Playwright" Version="1.58.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.5" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Physical" Version="10.0.5" />
<PackageVersion Include="Microsoft.Extensions.Localization.Abstractions" Version="10.0.5" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.5" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="10.0.5" />
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="10.4.0" />
</ItemGroup>
Expand Down Expand Up @@ -133,4 +134,4 @@
<!-- Transitive Packages -->
<PackageVersion Include="Microsoft.Bcl.Memory" Version="10.0.4" />
</ItemGroup>
</Project>
</Project>
19 changes: 0 additions & 19 deletions inspect.csx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace CrestApps.OrchardCore.AI;

public static class AIInvocationItemKeys
{
public const string LiveNavigationUrl = "LiveNavigationUrl";
public const string LivePageContextJson = "LivePageContextJson";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
using System.Text;
using System.Text.Json;

namespace CrestApps.OrchardCore.AI;

public static class LivePageContextPromptBuilder
{
public static string Append(string prompt, AIInvocationContext invocationContext)
{
if (string.IsNullOrWhiteSpace(prompt) || invocationContext is null)
{
return prompt;
}

if (!invocationContext.Items.TryGetValue(AIInvocationItemKeys.LivePageContextJson, out var rawContext) ||
rawContext is not string contextJson ||
string.IsNullOrWhiteSpace(contextJson))
{
return prompt;
}

var summary = BuildSummary(contextJson);
if (string.IsNullOrWhiteSpace(summary))
{
return prompt;
}

return $"{prompt}\n\n[Current visible page context]\n{summary}\n[/Current visible page context]";
}

public static void Store(AIInvocationContext invocationContext, string contextJson)
{
if (invocationContext is null || string.IsNullOrWhiteSpace(contextJson))
{
return;
}

try
{
using var document = JsonDocument.Parse(contextJson);
if (document.RootElement.ValueKind != JsonValueKind.Object)
{
return;
}

invocationContext.Items[AIInvocationItemKeys.LivePageContextJson] = contextJson;
}
catch (JsonException)
{
}
}

internal static string BuildSummary(string contextJson)
{
if (string.IsNullOrWhiteSpace(contextJson))
{
return null;
}

using var document = JsonDocument.Parse(contextJson);
var root = document.RootElement;
if (root.ValueKind != JsonValueKind.Object)
{
return null;
}

var builder = new StringBuilder();
AppendLine(builder, "URL", GetString(root, "url"));
AppendLine(builder, "Title", GetString(root, "title"));
AppendLine(builder, "Frame context", GetBoolean(root, "isParentContext") ? "parent page" : "current page");
AppendList(builder, "Headings", GetStringArray(root, "headings"), 12, 120);
AppendLinks(builder, root);
AppendList(builder, "Visible buttons", GetObjectStringArray(root, "buttons", "text"), 20, 120);
AppendLine(builder, "Visible text preview", Truncate(GetString(root, "textPreview"), 1500));

return builder.Length == 0 ? null : builder.ToString().TrimEnd();
}

private static void AppendLinks(StringBuilder builder, JsonElement root)
{
if (!root.TryGetProperty("links", out var linksElement) || linksElement.ValueKind != JsonValueKind.Array)
{
return;
}

var count = 0;
foreach (var link in linksElement.EnumerateArray())
{
if (count >= 40)
{
break;
}

var text = Truncate(GetString(link, "text"), 120);
var href = Truncate(GetString(link, "href"), 240);
if (string.IsNullOrWhiteSpace(text) && string.IsNullOrWhiteSpace(href))
{
continue;
}

if (count == 0)
{
if (builder.Length > 0)
{
builder.AppendLine();
}

builder.AppendLine("Visible links:");
}

builder.Append("- ");
if (!string.IsNullOrWhiteSpace(text))
{
builder.Append(text);
}
else
{
builder.Append("[no text]");
}

if (!string.IsNullOrWhiteSpace(href))
{
builder.Append(" -> ");
builder.Append(href);
}

var context = Truncate(GetString(link, "context"), 160);
if (!string.IsNullOrWhiteSpace(context))
{
builder.Append(" (context: ");
builder.Append(context);
builder.Append(')');
}

builder.AppendLine();
count++;
}
}

private static void AppendList(StringBuilder builder, string label, IEnumerable<string> values, int maxItems, int maxLength)
{
if (values is null)
{
return;
}

var appendedAny = false;
var count = 0;

foreach (var value in values)
{
var normalizedValue = Truncate(value?.Trim(), maxLength);
if (string.IsNullOrWhiteSpace(normalizedValue))
{
continue;
}

if (!appendedAny)
{
if (builder.Length > 0)
{
builder.AppendLine();
}

builder.AppendLine($"{label}:");
appendedAny = true;
}

builder.Append("- ");
builder.AppendLine(normalizedValue);
count++;

if (count >= maxItems)
{
break;
}
}
}

private static void AppendLine(StringBuilder builder, string label, string value)
{
if (string.IsNullOrWhiteSpace(value))
{
return;
}

if (builder.Length > 0)
{
builder.AppendLine();
}

builder.Append(label);
builder.Append(": ");
builder.Append(value.Trim());
}

private static string GetString(JsonElement element, string propertyName)
{
if (!element.TryGetProperty(propertyName, out var property) || property.ValueKind != JsonValueKind.String)
{
return null;
}

return property.GetString();
}

private static bool GetBoolean(JsonElement element, string propertyName)
{
if (!element.TryGetProperty(propertyName, out var property) || property.ValueKind is not JsonValueKind.True and not JsonValueKind.False)
{
return false;
}

return property.GetBoolean();
}

private static IEnumerable<string> GetStringArray(JsonElement element, string propertyName)
{
if (!element.TryGetProperty(propertyName, out var property) || property.ValueKind != JsonValueKind.Array)
{
yield break;
}

foreach (var item in property.EnumerateArray())
{
if (item.ValueKind == JsonValueKind.String)
{
yield return item.GetString();
}
}
}

private static IEnumerable<string> GetObjectStringArray(JsonElement element, string propertyName, string nestedPropertyName)
{
if (!element.TryGetProperty(propertyName, out var property) || property.ValueKind != JsonValueKind.Array)
{
yield break;
}

foreach (var item in property.EnumerateArray())
{
if (item.ValueKind != JsonValueKind.Object)
{
continue;
}

var value = GetString(item, nestedPropertyName);
if (!string.IsNullOrWhiteSpace(value))
{
yield return value;
}
}
}

private static string Truncate(string value, int maxLength)
{
if (string.IsNullOrWhiteSpace(value))
{
return value;
}

value = value.Trim();
if (value.Length <= maxLength)
{
return value;
}

return value[..maxLength];
}
}
Loading
Loading