Skip to content
Merged
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
11 changes: 5 additions & 6 deletions src/Microsoft.DotNet.Interactive.AIUtilities/GptFunction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,12 @@
using System.Text.Json;
using System.Text.Json.Serialization;


namespace Microsoft.DotNet.Interactive.AIUtilities;

public class GptFunction
{
private static readonly JsonSerializerOptions SerializerOptions;
public string Name { get; }
private readonly Delegate _function;
public string JsonSignature { get; }
public string? Description { get; }

static GptFunction()
{
Expand All @@ -31,6 +27,10 @@ internal GptFunction(string name, string jsonSignature, Delegate function, strin
Description = description;
}

public string? Description { get; }
public string JsonSignature { get; }
public string Name { get; }

public object? Execute(string parameterJson)
{
// parameters extraction
Expand All @@ -52,6 +52,7 @@ internal GptFunction(string name, string jsonSignature, Delegate function, strin

return Execute(json);
}

public object? Execute(JsonElement json)
{
// parameters extraction
Expand All @@ -70,8 +71,6 @@ internal GptFunction(string name, string jsonSignature, Delegate function, strin
}

return parameters;

throw new ArgumentException("arguments property is not found.");
}

private object? Deserialize(ParameterInfo parameterInfo, JsonElement jsonArgs)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.ComponentModel;
using Pocket;

namespace Microsoft.DotNet.Interactive.AIUtilities;

[EditorBrowsable(EditorBrowsableState.Never)]
public static class InteractiveExtension
{
public static void Load()
{
Logger.Log.Event();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,17 @@
<ItemGroup>
<PackageReference Include="Microsoft.DeepDev.TokenizerLib" Version="1.3.2" />
<PackageReference Include="System.Reactive" Version="$(SystemReactiveVersion)" />
<PackageReference Include="PocketLogger" Version="0.8.2">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<None Include="extension.dib" Pack="true" PackagePath="interactive-extensions/dotnet" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Microsoft.DotNet.Interactive\Microsoft.DotNet.Interactive.csproj" />
</ItemGroup>

</Project>
2 changes: 1 addition & 1 deletion src/Microsoft.DotNet.Interactive.AIUtilities/Text.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public static IObservable<string> ChunkByTokenCountWithOverlap(this IObservable<

return source.SelectMany(text =>
{
return System.Reactive.Linq.Observable.Create<string>(o =>
return Observable.Create<string>(o =>
{
var chunks = tokenizer.ChunkByTokenCountWithOverlap(text, maxTokenCount, overlapTokenCount);
foreach (var chunk in chunks)
Expand Down
5 changes: 3 additions & 2 deletions src/Microsoft.DotNet.Interactive.AIUtilities/Tokenizer.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.


using Microsoft.DeepDev;
using Pocket;

namespace Microsoft.DotNet.Interactive.AIUtilities;


public static class Tokenizer
{
public static int GetTokenCount(this ITokenizer tokenizer, string text)
Expand All @@ -18,6 +17,8 @@ public static int GetTokenCount(this ITokenizer tokenizer, string text)

public static async Task<ITokenizer> CreateAsync(TokenizerModel model)
{
Logger.Log.Event(properties: ("model", model));

var tokenizer = model switch
{
TokenizerModel.ada2 => await TokenizerBuilder.CreateByModelNameAsync("text-embedding-ada-002"),
Expand Down
3 changes: 3 additions & 0 deletions src/Microsoft.DotNet.Interactive.AIUtilities/extension.dib
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!csharp

await Microsoft.DotNet.Interactive.AIUtilities.InteractiveExtension.Load();
2 changes: 1 addition & 1 deletion src/Microsoft.DotNet.Interactive.Jupyter/JupyterKernel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ private static async Task<KernelInfoReply> RequestKernelInfo(IMessageSender send
return kernelInfoReply;
}

private async static Task<T> RunOnKernelAsync<T>(
private static async Task<T> RunOnKernelAsync<T>(
RequestMessage content,
IMessageSender sender,
IMessageReceiver receiver,
Expand Down
13 changes: 0 additions & 13 deletions src/Microsoft.DotNet.Interactive.Telemetry/ITelemetrySender.cs

This file was deleted.

104 changes: 45 additions & 59 deletions src/Microsoft.DotNet.Interactive.Telemetry/TelemetrySender.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,27 @@

namespace Microsoft.DotNet.Interactive.Telemetry;

public class TelemetrySender : ITelemetrySender
public class TelemetrySender
{
private readonly IFirstTimeUseNoticeSentinel _firstTimeUseNoticeSentinel;
private readonly string _eventsNamespace;
private readonly string _appInsightsConnectionString;
private readonly string _currentSessionId = null;
private TelemetryClient _client = null;
private Dictionary<string, string> _commonProperties = null;
private Dictionary<string, double> _commonMetrics = null;
private Task _trackEventTask = null;
private readonly bool _enabled;

private const string DefaultAppInsightsConnectionString = @"InstrumentationKey=b0dafad5-1430-4852-bc61-95c836b3e612;IngestionEndpoint=https://centralus-0.in.applicationinsights.azure.com/;LiveEndpoint=https://centralus.livediagnostics.monitor.azure.com/";
private const string DefaultAppInsightsConnectionString = "InstrumentationKey=b0dafad5-1430-4852-bc61-95c836b3e612;IngestionEndpoint=https://centralus-0.in.applicationinsights.azure.com/;LiveEndpoint=https://centralus.livediagnostics.monitor.azure.com/";
public const string TelemetryOptOutEnvironmentVariableName = "DOTNET_INTERACTIVE_CLI_TELEMETRY_OPTOUT";

public const string WelcomeMessage = @"Welcome to .NET Interactive!
---------------------
Telemetry
---------
The .NET Core tools collect usage data in order to help us improve your experience.The data is anonymous and doesn't include command-line arguments. The data is collected by Microsoft and shared with the community. You can opt-out of telemetry by setting the DOTNET_INTERACTIVE_CLI_TELEMETRY_OPTOUT environment variable to '1' or 'true' using your favorite shell.
";
public const string WelcomeMessage = $"""
Welcome to .NET Interactive!
---------------------
Telemetry
---------
The .NET tools collect usage data in order to help us improve your experience. The data is anonymous and doesn't include command-line arguments. The data is collected by Microsoft and shared with the community. You can opt-out of telemetry by setting the {TelemetryOptOutEnvironmentVariableName} environment variable to '1' or 'true' using your favorite shell.

""";

public TelemetrySender(
string productVersion,
Expand All @@ -46,47 +47,58 @@ public TelemetrySender(

_firstTimeUseNoticeSentinel = firstTimeUseNoticeSentinel ?? throw new ArgumentNullException(nameof(firstTimeUseNoticeSentinel));
_eventsNamespace = eventsNamespace;
_appInsightsConnectionString = appInsightsConnectionString ?? DefaultAppInsightsConnectionString;
var s = appInsightsConnectionString ?? DefaultAppInsightsConnectionString;

Enabled = !GetEnvironmentVariableAsBool(TelemetryOptOutEnvironmentVariableName) &&
firstTimeUseNoticeSentinel.Exists();
_enabled = !GetEnvironmentVariableAsBool(TelemetryOptOutEnvironmentVariableName) &&
firstTimeUseNoticeSentinel.Exists();

if (Enabled)
if (_enabled)
{
// Store the session ID in a static field so that it can be reused
_currentSessionId = Guid.NewGuid().ToString();

//initialize in task to offload to parallel thread
_trackEventTask = Task.Factory.StartNew(() => InitializeTelemetry(productVersion));
_trackEventTask = Task.Factory.StartNew(Initialize);
}
}

public bool Enabled { get; }
void Initialize()
{
try
{
var config = new TelemetryConfiguration();
config.ConnectionString = s;
_client = new TelemetryClient(config);
_client.Context.Session.Id = Guid.NewGuid().ToString();
_client.Context.Device.OperatingSystem = RuntimeEnvironment.OperatingSystem;

_commonProperties = new TelemetryCommonProperties(productVersion).GetTelemetryCommonProperties();
_commonMetrics = new Dictionary<string, double>();
}
catch (Exception e)
{
_client = null;
// we don't want to fail the tool if telemetry fails.
Debug.Fail(e.ToString());
}
}
}

public static bool SkipFirstTimeExperience => GetEnvironmentVariableAsBool(FirstTimeUseNoticeSentinel.SkipFirstTimeExperienceEnvironmentVariableName);

public static bool IsRunningInDockerContainer => GetEnvironmentVariableAsBool("DOTNET_RUNNING_IN_CONTAINER");

private static bool GetEnvironmentVariableAsBool(string name)
{
switch (Environment.GetEnvironmentVariable(name)?.ToLowerInvariant())
private static bool GetEnvironmentVariableAsBool(string name) =>
Environment.GetEnvironmentVariable(name)?.ToLowerInvariant() switch
{
case "true":
case "1":
case "yes":
return true;

default:
return false;
}
}
"true" => true,
"1" => true,
"yes" => true,
_ => false
};

public void TrackEvent(
string eventName,
IDictionary<string, string> properties = null,
IDictionary<string, double> measurements = null)
{
if (!Enabled)
if (!_enabled)
{
return;
}
Expand All @@ -100,38 +112,12 @@ public void TrackEvent(

public void TrackStartupEvent(ParseResult parseResult, StartupTelemetryEventBuilder eventBuilder)
{
if (parseResult is null || !Enabled || eventBuilder is null)
{
return;
}

foreach (var entry in eventBuilder.GetTelemetryEventsFrom(parseResult))
{
TrackEvent(entry.EventName, entry.Properties, entry.Metrics);
}
}

protected virtual void InitializeTelemetry(string productVersion)
{
try
{
var config = new TelemetryConfiguration();
config.ConnectionString = _appInsightsConnectionString;
_client = new TelemetryClient(config);
_client.Context.Session.Id = _currentSessionId;
_client.Context.Device.OperatingSystem = RuntimeEnvironment.OperatingSystem;

_commonProperties = new TelemetryCommonProperties(productVersion).GetTelemetryCommonProperties();
_commonMetrics = new Dictionary<string, double>();
}
catch (Exception e)
{
_client = null;
// we don't want to fail the tool if telemetry fails.
Debug.Fail(e.ToString());
}
}

protected virtual void DoTrackEvent(
string eventName,
IDictionary<string, string> properties = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Threading;
Expand Down
2 changes: 1 addition & 1 deletion src/dotnet-interactive.Tests/FakeTelemetrySender.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public FakeTelemetrySender(IFirstTimeUseNoticeSentinel firstTimeUseNoticeSentine

private static string GetInstrumentationKey() =>
_configuredConnectionString ??
$@"InstrumentationKey={Guid.NewGuid()};IngestionEndpoint=https://centralus-0.in.applicationinsights.azure.com/;LiveEndpoint=https://centralus.livediagnostics.monitor.azure.com/";
$"InstrumentationKey={Guid.NewGuid()};IngestionEndpoint=https://centralus-0.in.applicationinsights.azure.com/;LiveEndpoint=https://centralus.livediagnostics.monitor.azure.com/";

protected override void DoTrackEvent(
string eventName,
Expand Down
7 changes: 2 additions & 5 deletions src/dotnet-interactive.Tests/MagicCommandTests.about.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Threading;
using System.Threading.Tasks;
using FluentAssertions;
using Microsoft.DotNet.Interactive.Commands;
Expand All @@ -24,11 +22,10 @@ public async Task it_shows_the_product_name_and_version_information()
{
using var kernel = new CompositeKernel()
.UseAboutMagicCommand();

var result = await kernel.SendAsync(new SubmitCode("#!about"));

result.Events
.Should()
result.Events.Should()
.ContainSingle<DisplayedValueProduced>()
.Which
.FormattedValues
Expand Down
16 changes: 15 additions & 1 deletion src/dotnet-interactive.Tests/RuntimeTelemetryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ public RuntimeTelemetryTests()
{
new CSharpKernel().UseNugetDirective(false)
}
.UseTelemetrySender(_telemetrySender);
.UseTelemetrySender(_telemetrySender)
.UseNuGetExtensions(_telemetrySender);
}

public void Dispose() => _kernel.Dispose();
Expand Down Expand Up @@ -164,4 +165,17 @@ public async Task Package_and_version_number_are_sent_on_successful_package_load
new KeyValuePair<string, string>("PackageName", "nodatime".ToSha256HashWithNormalizedCasing()),
new KeyValuePair<string, string>("PackageVersion", "3.1.9".ToSha256Hash()));
}

[Fact(Skip = "Package version with extension telemetry needs to be published.")]
public async Task Extensions_can_send_telemetry_using_PocketLogger()
{
var results = await _kernel.SendAsync(new SubmitCode("""
#i "nuget:c:\temp\packages"
#r "nuget:Microsoft.DotNet.Interactive.AIUtilities,*-*"
"""));

results.Events.Should().NotContainErrors();

_telemetrySender.TelemetryEvents.Should().Contain(e => e.EventName == "Microsoft.DotNet.Interactive.AIUtilities.Load");
}
}
6 changes: 3 additions & 3 deletions src/dotnet-interactive/CommandLine/CommandLineParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ public static Parser Create(
rootCommand.AddCommand(StdIO());
rootCommand.AddCommand(NotebookParser());

var filter = new StartupTelemetryEventBuilder(Sha256Hasher.ToSha256HashWithNormalizedCasing);
var eventBuilder = new StartupTelemetryEventBuilder(Sha256Hasher.ToSha256HashWithNormalizedCasing);

return new CommandLineBuilder(rootCommand)
.UseDefaults()
Expand All @@ -145,7 +145,7 @@ public static Parser Create(
{
if (context.ParseResult.Errors.Count == 0)
{
telemetrySender.TrackStartupEvent(context.ParseResult, filter);
telemetrySender.TrackStartupEvent(context.ParseResult, eventBuilder);
}

// If sentinel does not exist, print the welcome message showing the telemetry notification.
Expand Down Expand Up @@ -505,7 +505,7 @@ private static CompositeKernel CreateKernel(
.UseDefaultMagicCommands()
.UseAboutMagicCommand()
.UseImportMagicCommand()
.UseNuGetExtensions();
.UseNuGetExtensions(telemetrySender);

kernel.AddKernelConnector(new ConnectNamedPipeCommand());
kernel.AddKernelConnector(new ConnectSignalRCommand());
Expand Down
Loading