Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
968bc20
+ SMTP attachments
AndreyKerchin1 Aug 7, 2025
d8d11a4
-
AndreyKerchin1 Aug 7, 2025
8f97e7a
Merge branch 'dev' of https://github.com/VIAcode/AICore into dev
AndreyKerchin1 Aug 8, 2025
2d8166c
Merge branch 'dev' of https://github.com/VIAcode/AICore into dev
AndreyKerchin1 Aug 10, 2025
c8243a9
Merge branch 'dev' of https://github.com/VIAcode/AICore into dev
AndreyKerchin1 Aug 13, 2025
66fbe02
Merge branch 'dev' of https://github.com/VIAcode/AICore into dev
AndreyKerchin1 Aug 25, 2025
572da1b
Merge branch 'dev' of https://github.com/VIAcode/AICore into dev
AndreyKerchin1 Aug 31, 2025
a405db9
Merge branch 'dev' of https://github.com/VIAcode/AICore into dev
AndreyKerchin1 Sep 2, 2025
59bb0b7
CombinedAuthorize for Delete Agent in order to add it to Pipeline
AndreyKerchin1 Sep 2, 2025
f7da66a
Merge branch 'dev' of https://github.com/VIAcode/AICore into dev
AndreyKerchin1 Sep 3, 2025
258b407
Merge branch 'dev' of https://github.com/VIAcode/AICore into dev
AndreyKerchin1 Sep 5, 2025
a7dbefd
Merge branch 'dev' of https://github.com/VIAcode/AICore into dev
AndreyKerchin1 Sep 10, 2025
7e60396
Merge branch 'dev' of https://github.com/VIAcode/AICore into dev
AndreyKerchin1 Sep 10, 2025
60fec05
Merge branch 'dev' of https://github.com/VIAcode/AICore into dev
AndreyKerchin1 Sep 14, 2025
85b565d
Merge branch 'dev' of https://github.com/VIAcode/AICore into dev
AndreyKerchin1 Sep 16, 2025
dec25cd
Merge branch 'dev' of https://github.com/VIAcode/AICore into dev
AndreyKerchin1 Sep 16, 2025
35ff399
Merge branch 'dev' of https://github.com/VIAcode/AICore into dev
AndreyKerchin1 Sep 21, 2025
427f6ff
Merge branch 'dev' of https://github.com/VIAcode/AICore into dev
AndreyKerchin1 Sep 21, 2025
0442d17
Merge branch 'dev' of https://github.com/VIAcode/AICore into dev
AndreyKerchin1 Sep 22, 2025
f511de9
Merge branch 'dev' of https://github.com/VIAcode/AICore into dev
AndreyKerchin1 Oct 4, 2025
1f09fc3
Merge branch 'dev' of https://github.com/VIAcode/AICore into dev
AndreyKerchin1 Oct 6, 2025
fc4f0f8
Merge branch 'dev' of https://github.com/VIAcode/AICore into dev
AndreyKerchin1 Oct 15, 2025
b47f93c
Merge branch 'dev' of https://github.com/VIAcode/AICore into dev
AndreyKerchin1 Oct 19, 2025
3ac288c
Merge branch 'dev' of https://github.com/VIAcode/AICore into dev
AndreyKerchin1 Oct 22, 2025
f8b103a
Merge branch 'dev' of https://github.com/VIAcode/AICore into dev
AndreyKerchin1 Oct 23, 2025
95e56b0
Merge branch 'dev' of https://github.com/VIAcode/AICore into dev
AndreyKerchin1 Nov 4, 2025
d764e9e
Merge branch 'dev' of https://github.com/VIAcode/AICore into dev
AndreyKerchin1 Nov 6, 2025
6f3dd70
Merge branch 'dev' of https://github.com/VIAcode/AICore into dev
AndreyKerchin1 Nov 6, 2025
70645b5
Merge branch 'dev' of https://github.com/VIAcode/AICore into dev
AndreyKerchin1 Nov 10, 2025
a23b187
Merge branch 'dev' of https://github.com/VIAcode/AICore into dev
AndreyKerchin1 Nov 16, 2025
51269fd
Merge branch 'dev' of https://github.com/VIAcode/AICore into dev
AndreyKerchin1 Nov 23, 2025
79e897c
Merge branch 'dev' of https://github.com/VIAcode/AICore into dev
AndreyKerchin1 Nov 25, 2025
aefb61f
Merge branch 'dev' of https://github.com/VIAcode/AICore into dev
AndreyKerchin1 Nov 30, 2025
df1f12a
Google Search Fix
AndreyKerchin1 Dec 4, 2025
3561318
review notes
AndreyKerchin1 Dec 4, 2025
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
15 changes: 7 additions & 8 deletions AiCoreApi/Common/ConnectionManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@ namespace AiCoreApi.Common
public class ConnectionManager: IConnectionManager
{
private readonly IConnectionProcessor _connectionProcessor;
private readonly IParametersHelper _parametersHelper;
private readonly IIngestionParametersHelper _ingestionParametersHelper;

public ConnectionManager(
IConnectionProcessor connectionProcessor,
IParametersHelper parametersHelper)
IConnectionProcessor connectionProcessor,
IIngestionParametersHelper ingestionParametersHelper)
{
_connectionProcessor = connectionProcessor;
_parametersHelper = parametersHelper;
_ingestionParametersHelper = ingestionParametersHelper;
}

private volatile List<ConnectionModel>? _cache;
private List<ConnectionModel>? _cache;
Comment thread
AKercha1 marked this conversation as resolved.
private readonly object _cacheLock = new();

public async Task<List<ConnectionModel>> GetConnectionModels()
Expand Down Expand Up @@ -54,7 +54,7 @@ public async Task<List<ConnectionModel>> GetConnectionModels()
WorkspaceId = connection.WorkspaceId,
Content = new Dictionary<string, string>(connection.Content),
};
return await _parametersHelper.ApplySecrets(connectionCopy);
return await _ingestionParametersHelper.ApplySecrets(connectionCopy);
}

public async Task<ConnectionModel?> GetConnection(int? workspaceId = null, int? connectionId = null, ConnectionType? connectionType = null, bool isLlmConnection = false, bool isEmbeddingConnection = false, string? connectionName = null)
Expand All @@ -67,8 +67,7 @@ public async Task<List<ConnectionModel>> GetConnectionModels()
&& (!isLlmConnection || c.Type.IsLlmConnection())
&& (!isEmbeddingConnection || c.Type.IsEmbeddingConnection())
&& (string.IsNullOrEmpty(connectionName) || c.Name == connectionName)
)
?? null;
);
return connection;
}

Expand Down
21 changes: 8 additions & 13 deletions AiCoreApi/Common/ParametersHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,41 +6,36 @@

namespace AiCoreApi.Common
{
public class ParametersHelper : ParametersHelperBase, IParametersHelper
public class IngestionParametersHelper : ParametersHelperBase, IIngestionParametersHelper
{
public ParametersHelper(
public IngestionParametersHelper(
RequestAccessor requestAccessor,
ICacheAccessor cacheAccessor,
ResponseAccessor responseAccessor,
ILogger<ParametersHelper> logger,
ILogger<IngestionParametersHelper> logger,
IEntraTokenProvider entraTokenProvider)
: base(requestAccessor, cacheAccessor, responseAccessor, logger, entraTokenProvider)
{
}

protected override string ResolveUnknownKey(string key)
{
return $"{{{{{key}}}}}";
}
}

public interface IParametersHelper
public interface IIngestionParametersHelper
{
Task<ConnectionModel> ApplySecrets(ConnectionModel connectionModel);
Task<string> ApplySecret(string value);
Task<string> ApplyParametersAsync(string text, string debugSource, Dictionary<string, string>? parameters, Dictionary<string, string>? additionalParameters = null);
}

public class IngestionParametersHelper : ParametersHelperBase, IIngestionParametersHelper
public class ParametersHelper : ParametersHelperBase, IParametersHelper
{
private readonly IIngestionProcessor _ingestionProcessor;
private readonly IDataIngestionWorkerFactory _workerFactory;

public IngestionParametersHelper(
public ParametersHelper(
RequestAccessor requestAccessor,
ICacheAccessor cacheAccessor,
ResponseAccessor responseAccessor,
ILogger<IngestionParametersHelper> logger,
ILogger<ParametersHelper> logger,
IEntraTokenProvider entraTokenProvider,
IIngestionProcessor ingestionProcessor,
IDataIngestionWorkerFactory workerFactory)
Expand Down Expand Up @@ -89,7 +84,7 @@ private async Task<string> ResolveDataSourceValueAsync(string key)
}
}

public interface IIngestionParametersHelper
public interface IParametersHelper
{
Task<ConnectionModel> ApplySecrets(ConnectionModel connectionModel);
Task<string> ApplySecret(string value);
Expand Down
2 changes: 1 addition & 1 deletion AiCoreApi/SemanticKernel/Agents/BaseAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public abstract class BaseAgent : IDoCallWrapperAgent
private readonly ResponseAccessor _responseAccessor;
private readonly RequestAccessor _requestAccessor;
private readonly MonitoringConfig _monitoringConfig;
private readonly IIngestionParametersHelper _parametersHelper;
private readonly IParametersHelper _parametersHelper;

private readonly ILogger<BaseAgent> _logger;
private Dictionary<string, string>? _parameters;
Expand Down
6 changes: 3 additions & 3 deletions AiCoreApi/SemanticKernel/Agents/BaseAgentHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public BaseAgentHelper(
RequestAccessor requestAccessor,
ResponseAccessor responseAccessor,
MonitoringConfig monitoringConfig,
IIngestionParametersHelper parametersHelper
IParametersHelper parametersHelper
)
{
AgentsProcessor = agentsProcessor;
Expand All @@ -25,7 +25,7 @@ IIngestionParametersHelper parametersHelper
public RequestAccessor RequestAccessor { get; }
public ResponseAccessor ResponseAccessor { get; }
public MonitoringConfig MonitoringConfig { get; }
public IIngestionParametersHelper ParametersHelper { get; }
public IParametersHelper ParametersHelper { get; }
}

public interface IBaseAgentHelper
Expand All @@ -34,6 +34,6 @@ public interface IBaseAgentHelper
RequestAccessor RequestAccessor { get; }
ResponseAccessor ResponseAccessor { get; }
MonitoringConfig MonitoringConfig { get; }
IIngestionParametersHelper ParametersHelper { get; }
IParametersHelper ParametersHelper { get; }
}
}
118 changes: 109 additions & 9 deletions AiCoreApi/SemanticKernel/Agents/GoogleSearchApiAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Text.Json;
using AiCoreApi.Common.Extensions;
using System.Text.Encodings.Web;
using Microsoft.Playwright;

namespace AiCoreApi.SemanticKernel.Agents
{
Expand All @@ -26,9 +27,10 @@ private static class AgentContentParameters
public const string QueryString = "queryString";
public const string GoogleConnection = "googleSearchApiConnection";
public const string MaxContentLength = "maxContentLength";
public const string Count = "count";
public const string Offset = "offset";
public const string Count = "count";
public const string Offset = "offset";
public const string OutputType = "outputType";
public const string WaitTimeout = "waitTimeout";
}

private const int DefaultMaxContentLength = 16384;
Expand All @@ -37,19 +39,22 @@ private static class AgentContentParameters
private readonly ResponseAccessor _responseAccessor;
private readonly IHttpClientFactory _httpClientFactory;
private readonly IConnectionProcessor _connectionProcessor;
private readonly ExtendedConfig _extendedConfig;

public GoogleSearchApiAgent(
IBaseAgentHelper baseAgentHelper,
RequestAccessor requestAccessor,
ResponseAccessor responseAccessor,
IHttpClientFactory httpClientFactory,
IConnectionProcessor connectionProcessor,
ExtendedConfig extendedConfig,
ILogger<GoogleSearchApiAgent> logger) : base(baseAgentHelper, logger)
{
_requestAccessor = requestAccessor;
_responseAccessor = responseAccessor;
_httpClientFactory = httpClientFactory;
_connectionProcessor = connectionProcessor;
_extendedConfig = extendedConfig;
}

public override async Task<string> DoCall(AgentModel agent, Dictionary<string, string> parameters)
Expand All @@ -75,6 +80,7 @@ public override async Task<string> DoCall(AgentModel agent, Dictionary<string, s
var count = int.Parse(await GetParameterValueAsync(AgentContentParameters.Count));
var offset = int.Parse(await GetParameterValueAsync(AgentContentParameters.Offset));
var outputType = agent.Content.TryGetValue(AgentContentParameters.OutputType, out var ot) ? ot.Value : "snippetTexts";
var waitTimeout = Convert.ToInt32(await GetParameterValueAsync(AgentContentParameters.WaitTimeout, "10000"));
var results = await DoSearchAsync(queryString, googleConnection.Content["apiKey"], googleConnection.Content["googleCxId"], count, offset);

string result;
Expand All @@ -92,13 +98,63 @@ public override async Task<string> DoCall(AgentModel agent, Dictionary<string, s
if (text.Length > int.Parse(maxContentLength))
text = text.Substring(0, int.Parse(maxContentLength));

pages.Add(new Dictionary<string, string> { { "url", page.Url }, { "name", page.Name }, { "text", text } });
pages.Add(new Dictionary<string, string>
{
{ "url", page.Url },
{ "name", page.Name },
{ "text", text }
});
}
result = JsonSerializer.Serialize(pages, new JsonSerializerOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping });

result = JsonSerializer.Serialize(pages,
new JsonSerializerOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping });
}
else if (outputType == "pagesJsonPlaywright")
{
// NEW MODE � PLAYWRIGHT PAGE LOADING

PlaywrightInstall.EnsureInstalled();
using var playwright = await Playwright.CreateAsync();

await using var browser = await playwright.Chromium.LaunchAsync(new() { Headless = true });

await using var context = await browser.NewContextAsync(new BrowserNewContextOptions
{
UserAgent = "Mozilla/5.0 (compatible; GoogleSearchApiAgent/Playwright)",
Proxy = string.IsNullOrEmpty(_extendedConfig.Proxy)
? null
: new Proxy { Server = _extendedConfig.Proxy },
IgnoreHTTPSErrors = true
});

var pageObj = await context.NewPageAsync();

var pages = new List<Dictionary<string, string>>();

foreach (var page in results)
{
var text = await CrawlWithPlaywrightAsync(page.Url, pageObj, waitTimeout);

if (text.Length > int.Parse(maxContentLength))
text = text.Substring(0, int.Parse(maxContentLength));

pages.Add(new Dictionary<string, string>
{
{ "url", page.Url },
{ "name", page.Name },
{ "text", text }
});
}

result = JsonSerializer.Serialize(pages,
new JsonSerializerOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping });
}
else
{
result = JsonSerializer.Serialize(results.Select(r => r.Snippet).ToList(), new JsonSerializerOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping });
// snippetTexts � default
result = JsonSerializer.Serialize(
results.Select(r => r.Snippet).ToList(),
new JsonSerializerOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping });
}

_responseAccessor.AddDebugMessage(_debugMessageSenderName, "Execute Query String Result", result);
Expand All @@ -125,15 +181,21 @@ private async Task<List<WebPage>> DoSearchAsync(string query, string apiKey, str
{
results.Add(new WebPage
{
Name = item.GetProperty("title").GetString() ?? "",
Url = item.GetProperty("link").GetString() ?? "",
Snippet = item.GetProperty("snippet").GetString() ?? ""
Name = SafeGet(item, "title"),
Url = SafeGet(item, "link"),
Snippet = SafeGet(item, "snippet")
});
}

return results;
}

private static string SafeGet(JsonElement el, string propName)
{
return el.TryGetProperty(propName, out var p)
? p.GetString() ?? ""
: "";
}

private async Task<string> CrawlPageTextAsync(string url)
{
try
Expand Down Expand Up @@ -166,6 +228,44 @@ private async Task<string> CrawlPageTextAsync(string url)
}
}

private async Task<string> CrawlWithPlaywrightAsync(string url, IPage page, int waitTimeout)
{
try
{
await page.GotoAsync(url, new() { Timeout = waitTimeout });

var allText = new List<string>();

async Task ProcessFrame(IFrame frame)
{
var content = await frame.ContentAsync();
var doc = new HtmlDocument();
doc.LoadHtml(content);

doc.DocumentNode.Descendants()
.Where(n => n.Name is "script" or "style")
.ToList()
.ForEach(n => n.Remove());

var frameText = HtmlEntity.DeEntitize(doc.DocumentNode.InnerText);

allText.AddRange(
frameText.Split('\n')
.Select(x => x.Trim())
.Where(x => !string.IsNullOrWhiteSpace(x)));
}

foreach (var frame in page.Frames)
await ProcessFrame(frame);

return string.Join("\n", allText);
}
catch (Exception ex)
{
_responseAccessor.AddDebugMessage(_debugMessageSenderName, "Playwright Error", $"Failed to read {url}, {ex.Message}");
return string.Empty;
}
}

private async Task<HttpResponseMessage> SendGetRequestAsync(Uri uri, CancellationToken cancellationToken = default)
{
Expand Down