public async Task SendAsync(KernelCommand kernelCommand, CancellationToken cancellationToken) { // FIX: remove this one as this is for backward compatibility await _sender.SendAsync("submitCommand", KernelCommandEnvelope.Serialize(KernelCommandEnvelope.Create(kernelCommand)), cancellationToken : cancellationToken); await _sender.SendAsync("kernelCommandFromServer", KernelCommandEnvelope.Serialize(KernelCommandEnvelope.Create(kernelCommand)), cancellationToken : cancellationToken); }
public void When_command_type_registered_then_kernel_registers_envelope_type_for_serialization(bool withHandler) { KernelCommandEnvelope.RegisterDefaults(); using var kernel = new FakeKernel(); if (withHandler) { kernel.RegisterCommandHandler <CustomCommandTypes.FirstSubmission.MyCommand>( (_, _) => Task.CompletedTask); } else { kernel.RegisterCommandType <CustomCommandTypes.FirstSubmission.MyCommand>(); } var originalCommand = new CustomCommandTypes.FirstSubmission.MyCommand("xyzzy"); string envelopeJson = KernelCommandEnvelope.Serialize(originalCommand); var roundTrippedCommandEnvelope = KernelCommandEnvelope.Deserialize(envelopeJson); roundTrippedCommandEnvelope .Command .Should() .BeOfType <CustomCommandTypes.FirstSubmission.MyCommand>() .Which .Info .Should() .Be(originalCommand.Info); }
public async Task SendAsync(KernelCommand kernelCommand, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); _sender.WriteMessage(KernelCommandEnvelope.Serialize(KernelCommandEnvelope.Create(kernelCommand))); await _sender.FlushAsync(cancellationToken); }
public static void RegisterEventsAndCommands() { // register commands and event with serialization var commandTypes = typeof(CSharpProjectKernel).Assembly.ExportedTypes .Where(t => !t.IsAbstract && !t.IsInterface) .Where(t => typeof(KernelCommand).IsAssignableFrom(t)) .OrderBy(t => t.Name) .ToList(); var eventTypes = typeof(CSharpProjectKernel).Assembly.ExportedTypes .Where(t => !t.IsAbstract && !t.IsInterface) .Where(t => typeof(KernelEvent).IsAssignableFrom(t)) .OrderBy(t => t.Name) .ToList(); foreach (var commandType in commandTypes) { KernelCommandEnvelope.RegisterCommand(commandType); } foreach (var eventType in eventTypes) { KernelEventEnvelope.RegisterEvent(eventType); } }
public void Dispose() { KernelCommandEnvelope.ResetToDefaults(); KernelEventEnvelope.ResetToDefaults(); _host.Value.Dispose(); _host = null; }
public static TKernel UseVSCodeCommands <TKernel>(this TKernel kernel) where TKernel : Kernel { kernel.RegisterCommandType <GetInput>(); KernelCommandEnvelope.RegisterCommand <GetInput>(); KernelEventEnvelope.RegisterEvent <InputProduced>(); return(kernel); }
public CommandLineParserTests(ITestOutputHelper output) { KernelCommandEnvelope.ResetToDefaults(); KernelEventEnvelope.ResetToDefaults(); _output = output; _serviceCollection = new ServiceCollection(); _parser = CommandLineParser.Create( _serviceCollection, startServer: (options, invocationContext) => { _startOptions = options; }, jupyter: (startupOptions, console, startServer, context) => { _startOptions = startupOptions; return(Task.FromResult(1)); }, startStdIO: (startupOptions, kernel, console) => { _startOptions = startupOptions; return(Task.FromResult(1)); }, startHttp: (startupOptions, console, startServer, context) => { _startOptions = startupOptions; return(Task.FromResult(1)); }, telemetry: new FakeTelemetry(), firstTimeUseNoticeSentinel: new NopFirstTimeUseNoticeSentinel()); _connectionFile = new FileInfo(Path.GetTempFileName()); _kernelSpecInstallPath = new DirectoryInfo(Path.Combine(Path.GetTempPath(), Path.GetRandomFileName())); }
public void Create_creates_envelope_of_the_correct_type() { IKernelCommand command = new SubmitCode("display(123)"); var envelope = KernelCommandEnvelope.Create(command); envelope.Should().BeOfType<KernelCommandEnvelope<SubmitCode>>(); }
public void Create_creates_envelope_with_reference_to_original_command() { IKernelCommand command = new SubmitCode("display(123)"); var envelope = KernelCommandEnvelope.Create(command); envelope.Command.Should().BeSameAs(command); }
public static T UseQuitCommand <T>(this T kernel, IDisposable disposeOnQuit, CancellationToken cancellationToken) where T : Kernel { Quit.DisposeOnQuit = disposeOnQuit; KernelCommandEnvelope.RegisterCommandType <Quit>(nameof(Quit)); cancellationToken.Register(async() => { await kernel.SendAsync(new Quit()); }); return(kernel); }
public async Task stdio_command_extends_the_protocol_with_quit_command() { await _parser.InvokeAsync("stdio"); var envelope = KernelCommandEnvelope.Deserialize(@"{ commandType: ""Quit"", command : { } }"); envelope.Command.Should() .BeOfType <Quit>(); }
public async Task HandleAsync(SubmitCode command, KernelInvocationContext context) { var envelope = KernelCommandEnvelope.Create(command); _clientStream.WriteMessage(KernelCommandEnvelope.Serialize(envelope)); await _clientStream.FlushAsync(); await PollEvents(envelope.Token); }
public static Task WriteAsync( this StandardIOKernelServer server, KernelCommand command) { var json = KernelCommandEnvelope.Serialize( KernelCommandEnvelope.Create(command)); return(server.WriteAsync(json)); }
static CSharpProjectKernel() { // register commands and event with serialization KernelCommandEnvelope.RegisterCommand <OpenProject>(); KernelCommandEnvelope.RegisterCommand <OpenDocument>(); KernelCommandEnvelope.RegisterCommand <CompileProject>(); KernelEventEnvelope.RegisterEvent <DocumentOpened>(); KernelEventEnvelope.RegisterEvent <AssemblyProduced>(); }
internal async Task ExecuteEnvelope(string token) { var envelope = EnvelopeHelper.GetAndDeleteEnvelope(token); if (envelope != null) { TrackEvents(token); var serialized = KernelCommandEnvelope.Serialize(envelope); await DotNetInteractiveProcess.StandardInput.WriteLineAsync(serialized); } }
public void Command_contract_has_not_been_broken(KernelCommand command) { var _configuration = new Configuration() .UsingExtension($"{command.GetType().Name}.json") .SetInteractive(Debugger.IsAttached); command.SetToken("the-token"); var json = KernelCommandEnvelope.Serialize(command); this.Assent(json, _configuration); }
public async Task when_a_KernelCommandEnvelope_is_received_it_reads_the_command() { var kernelCommand = new SubmitCode("x=1"); var message = KernelCommandEnvelope.Serialize(KernelCommandEnvelope.Create(kernelCommand)); using var stringReader = new StringReader(message); var receiver = new KernelCommandAndEventTextReceiver(stringReader); var d = await receiver.CommandsAndEventsAsync(CancellationToken.None).FirstAsync(); d.Command.Should().BeEquivalentTo(kernelCommand); }
public async Task when_a_KernelCommand_is_sent_it_writes_a_KernelCommandEnvelope() { var kernelCommand = new SubmitCode("x=1"); var buffer = new StringBuilder(); var sender = new KernelCommandAndEventTextStreamSender(new StringWriter(buffer)); await sender.SendAsync(kernelCommand, CancellationToken.None); var envelopeMessage = buffer.ToString(); envelopeMessage.Should() .BeEquivalentTo(KernelCommandEnvelope.Serialize(KernelCommandEnvelope.Create(kernelCommand)) + KernelCommandAndEventTextStreamSender.Delimiter); }
public SerializationTests(ITestOutputHelper output) { _output = output; KernelCommandEnvelope.RegisterCommand <OpenProject>(); KernelCommandEnvelope.RegisterCommand <OpenDocument>(); KernelCommandEnvelope.RegisterCommand <CompileProject>(); KernelEventEnvelope.RegisterEvent <DocumentOpened>(); KernelEventEnvelope.RegisterEvent <AssemblyProduced>(); KernelCommandEnvelope.RegisterCommand <GetInput>(); KernelEventEnvelope.RegisterEvent <InputProduced>(); }
public async Task kernel_server_honors_log_path() { using var logPath = DisposableDirectory.Create(); using var outputReceived = new ManualResetEvent(false); var errorLines = new List <string>(); // start as external process var dotnet = new Dotnet(logPath.Directory); using var kernelServerProcess = dotnet.StartProcess( args: $@"""{typeof(Program).Assembly.Location}"" kernel-server --log-path ""{logPath.Directory.FullName}""", output: _line => { outputReceived.Set(); }, error: errorLines.Add); // wait for log file to be created var logFile = await logPath.Directory.WaitForFile( timeout : TimeSpan.FromSeconds(2), predicate : _file => true); // any matching file is the one we want errorLines.Should().BeEmpty(); logFile.Should().NotBeNull("unable to find created log file"); // submit code var submission = new SubmitCode("1+1"); var submissionJson = KernelCommandEnvelope.Serialize(KernelCommandEnvelope.Create(submission)); await kernelServerProcess.StandardInput.WriteLineAsync(submissionJson); await kernelServerProcess.StandardInput.FlushAsync(); // wait for output to proceed var gotOutput = outputReceived.WaitOne(timeout: TimeSpan.FromSeconds(2)); gotOutput.Should().BeTrue("expected to receive on stdout"); // kill kernelServerProcess.StandardInput.Close(); // simulate Ctrl+C await Task.Delay(TimeSpan.FromSeconds(2)); // allow logs to be flushed kernelServerProcess.Kill(); kernelServerProcess.WaitForExit(2000).Should().BeTrue(); errorLines.Should().BeEmpty(); // check log file for expected contents (await logFile.WaitForFileCondition( timeout: TimeSpan.FromSeconds(2), predicate: file => file.Length > 0)) .Should().BeTrue("expected non-empty log file"); var logFileContents = File.ReadAllText(logFile.FullName); logFileContents.Should().Contain("ℹ OnAssemblyLoad: "); }
public void All_command_types_are_round_trip_serializable(IKernelCommand command) { var originalEnvelope = KernelCommandEnvelope.Create(command); var json = KernelCommandEnvelope.Serialize(originalEnvelope); _output.WriteLine(json); var deserializedEnvelope = KernelCommandEnvelope.Deserialize(json); deserializedEnvelope .Should() .BeEquivalentTo(originalEnvelope); }
public Task OnLoadAsync(Kernel kernel) { if (kernel is CompositeKernel compositeKernel) { var outputOption = new Option <FileInfo>( new [] { "-o", "--output" }, description: "The name of the file to write the transcript to"); var record = new Command( "#!record", "Records a replayable transcript of code submissions.") { outputOption }; record.Handler = CommandHandler.Create((InvocationContext ctx) => { var outputFile = ctx.ParseResult.GetValueForOption(outputOption); compositeKernel.AddMiddleware(async(command, context, next) => { var json = KernelCommandEnvelope.Serialize(command); await File.AppendAllLinesAsync( outputFile.FullName, new[] { json }); await next(command, context); }); return(Task.CompletedTask); }); compositeKernel.AddDirective(record); } KernelInvocationContext.Current?.Display( new HtmlString(@"<details><summary>Use the <code>#!record</code> magic command to keep a transcript of the code you run.</summary> <p>Once you enable transcripts using <code>#!record</code>, each code submission (including re-running cells) is recorded in the specified file. The JSON format used is the same format recognized by the .NET Interactive <code>stdio</code> and <code>http</code> APIs and can be used to replay an interactive session via automation.</p> <img src=""https://user-images.githubusercontent.com/547415/109562409-343b1300-7a93-11eb-8ebf-79bb6af028cf.png"" width=""75%"" /> </details>"), "text/html"); return(Task.CompletedTask); }
public void All_command_types_are_round_trip_serializable(KernelCommand command) { var originalEnvelope = KernelCommandEnvelope.Create(command); var json = KernelCommandEnvelope.Serialize(originalEnvelope); _output.WriteLine(json); var deserializedEnvelope = KernelCommandEnvelope.Deserialize(json); deserializedEnvelope .Should() .BeEquivalentTo(originalEnvelope, o => o.Excluding(e => e.Command.Properties) .Excluding(e => e.Command.Handler)); }
public async IAsyncEnumerable <CommandOrEvent> CommandsOrEventsAsync([EnumeratorCancellation] CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { KernelCommand kernelCommand = null; KernelEvent kernelEvent = null; var message = await ReadMessageAsync(cancellationToken); if (string.IsNullOrWhiteSpace(message)) { continue; } var isParseError = false; try { var jsonObject = JsonDocument.Parse(message).RootElement; if (IsEventEnvelope(jsonObject)) { var kernelEventEnvelope = KernelEventEnvelope.Deserialize(jsonObject); kernelEvent = kernelEventEnvelope.Event; } else if (IsCommandEnvelope(jsonObject)) { var kernelCommandEnvelope = KernelCommandEnvelope.Deserialize(jsonObject); kernelCommand = kernelCommandEnvelope.Command; } else { kernelEvent = new DiagnosticLogEntryProduced( $"Expected {nameof(KernelCommandEnvelope)} or {nameof(KernelEventEnvelope)} but received: \n{message}", KernelCommand.None); isParseError = true; } } catch (Exception ex) { kernelEvent = new DiagnosticLogEntryProduced( $"Error while parsing Envelope: {message} \n{ex.Message}", KernelCommand.None); isParseError = true; } yield return(kernelCommand is null ? new CommandOrEvent(kernelEvent, isParseError) : new CommandOrEvent(kernelCommand)); } }
protected override async Task <CommandOrEvent> ReadCommandOrEventAsync(CancellationToken cancellationToken) { KernelCommand kernelCommand = null; KernelEvent kernelEvent = null; var message = await ReadMessageAsync(cancellationToken); if (string.IsNullOrWhiteSpace(message)) { return(null); } var isParseError = false; try { var jsonObject = JsonDocument.Parse(message).RootElement; if (IsEventEnvelope(jsonObject)) { var kernelEventEnvelope = KernelEventEnvelope.Deserialize(jsonObject); kernelEvent = kernelEventEnvelope.Event; } else if (IsCommandEnvelope(jsonObject)) { var kernelCommandEnvelope = KernelCommandEnvelope.Deserialize(jsonObject); kernelCommand = kernelCommandEnvelope.Command; } else { kernelEvent = new DiagnosticLogEntryProduced( $"Expected {nameof(KernelCommandEnvelope)} or {nameof(KernelEventEnvelope)} but received: \n{message}", KernelCommand.None); isParseError = true; } } catch (Exception ex) { kernelEvent = new DiagnosticLogEntryProduced( $"Error while parsing Envelope: {message} \n{ex.Message}", KernelCommand.None); isParseError = true; } return(kernelCommand is null ? new CommandOrEvent(kernelEvent, isParseError) : new CommandOrEvent(kernelCommand)); }
public void When_command_type_reregistered_with_changed_type_command_then_kernel_registers_updated_envelope_type_for_serialization(bool withHandler) { // Notebook authors should be able to develop their custom commands experimentally and progressively, // so we don't want any "you have to restart your kernel now" situations just because you already // called RegisterCommandHandler once for a particular command type. KernelCommandEnvelope.RegisterDefaults(); using var kernel = new FakeKernel(); if (withHandler) { kernel.RegisterCommandHandler <CustomCommandTypes.FirstSubmission.MyCommand>( (_, _) => Task.CompletedTask); kernel.RegisterCommandHandler <CustomCommandTypes.SecondSubmission.MyCommand>( (_, _) => Task.CompletedTask); } else { kernel.RegisterCommandType <CustomCommandTypes.FirstSubmission.MyCommand>(); kernel.RegisterCommandType <CustomCommandTypes.SecondSubmission.MyCommand>(); } var originalCommand = new CustomCommandTypes.SecondSubmission.MyCommand("xyzzy", 42); string envelopeJson = KernelCommandEnvelope.Serialize(originalCommand); var roundTrippedCommandEnvelope = KernelCommandEnvelope.Deserialize(envelopeJson); roundTrippedCommandEnvelope .Command .Should() .BeOfType <CustomCommandTypes.SecondSubmission.MyCommand>() .Which .Info .Should() .Be(originalCommand.Info); roundTrippedCommandEnvelope .Command .As <CustomCommandTypes.SecondSubmission.MyCommand>() .AdditionalProperty .Should() .Be(originalCommand.AdditionalProperty); }
public Task OnLoadAsync(IKernel kernel) { if (kernel is CompositeKernel kernelBase) { var record = new Command( "#!record", "Records a replayable transcript of code submissions.") { new Option <FileInfo>( new [] { "-o", "--output" }, description: "The name of the file to write the transcript to") }; record.Handler = CommandHandler.Create <FileInfo>(output => { kernelBase.AddMiddleware(async(command, context, next) => { var json = KernelCommandEnvelope.Serialize(command); await File.AppendAllLinesAsync( output.FullName, new[] { json }); await next(command, context); }); return(Task.CompletedTask); }); kernelBase.AddDirective(record); } return(Task.CompletedTask); }
public async Task SubmitCommand(string kernelCommandEnvelope) { var envelope = KernelCommandEnvelope.Deserialize(kernelCommandEnvelope); var command = envelope.Command; await _connection.Kernel.SendAsync(command); }
public Task SendAsync(KernelCommand kernelCommand, CancellationToken cancellationToken) { _onSendAsync?.Invoke(new CommandOrEvent(KernelCommandEnvelope.Deserialize(KernelCommandEnvelope.Serialize(kernelCommand)).Command)); return(Task.CompletedTask); }
#pragma warning disable CS1998 protected override async Task OnMessageActivityAsync(ITurnContext <IMessageActivity> turnContext, CancellationToken cancellationToken) { var value = turnContext.Activity; var attachments = turnContext.Activity.Attachments; if (turnContext.Activity.Value == null) // someone typed in something, it isn't a card { var content = turnContext.Activity.Text; var code = CheckForCode(content); var conversationReference = turnContext.Activity.GetConversationReference(); var mention = new Mention { Mentioned = turnContext.Activity.From, Text = $"<at>{turnContext.Activity.From.Name}</at>", }; if (!string.IsNullOrEmpty(code)) { if (DotNetInteractiveProcessRunner.Instance.CanExecuteCode) { var submissionToken = Guid.NewGuid().ToString("N"); var messageText = string.Empty; var user = UserGame.GetOrCreateUser(mention, turnContext.Activity.From); if (UserGame.CurrentChatUser?.Id != user.Id) { UserGame.CurrentChatUser = user; messageText = $"Hey {mention.Text} It looks like you're typing some code. Let me run it for you! 😊"; } else { messageText = UserGame.GetMessageForUser(mention); } await turnContext.Adapter.ContinueConversationAsync(_botId, conversationReference, async (context, token) => { var message = MessageFactory.Text(messageText); if (messageText.Contains(mention.Text)) { message.Entities.Add(mention); } await context.SendActivityAsync(message, token); }, cancellationToken); // build the envelope var submitCode = new SubmitCode(code); submitCode.SetToken(submissionToken); var envelope = KernelCommandEnvelope.Create(submitCode); var channel = ContentSubjectHelper.GetOrCreateChannel(submissionToken); EnvelopeHelper.StoreEnvelope(submissionToken, envelope); var cardSent = false; channel .Timeout(DateTimeOffset.UtcNow.Add(TimeSpan.FromMinutes(1))) .Buffer(TimeSpan.FromSeconds(1)) .Subscribe( onNext: async formattedValues => { turnContext.Adapter.ContinueConversationAsync(_botId, conversationReference, (context, token) => { if (formattedValues.Count > 0) { var hasHtml = formattedValues.Any(f => f.MimeType == HtmlFormatter.MimeType); if (hasHtml) { if (!cardSent) { cardSent = true; var card = new HeroCard { Title = "Your output is too awesome 😎", Subtitle = "Use the viewer to see it.", Buttons = new List <CardAction> { new TaskModuleAction("Open Viewer", new { data = submissionToken }) }, }.ToAttachment(); var message = MessageFactory.Attachment(card); context.SendActivityAsync(message, token).Wait(); } } else { var content = string.Join("\n", formattedValues.Select(f => f.Value)); var message = MessageFactory.Text($"```\n{content.HtmlEncode()}"); context.SendActivityAsync(message, token).Wait(); } } return(Task.CompletedTask); }, cancellationToken).Wait(); }, onCompleted: async() => { await turnContext.Adapter.ContinueConversationAsync(_botId, conversationReference, async(context, token) => { await Task.Delay(1000); var message = MessageFactory.Text($"{mention.Text} all done here 👍"); message.Entities.Add(mention); await context.SendActivityAsync(message, token); }, cancellationToken); }, onError: async error => { await turnContext.Adapter.ContinueConversationAsync(_botId, conversationReference, async(context, token) => { await Task.Delay(1000); var message = MessageFactory.Text($@"{mention.Text} there were some issues 👎 :\n {error.Message}"); message.Entities.Add(mention); await context.SendActivityAsync(message, token); }, cancellationToken); }); user.IncrementCodeSubmissionCount(); await DotNetInteractiveProcessRunner.Instance.ExecuteEnvelope(submissionToken); } else { await turnContext.Adapter.ContinueConversationAsync(_botId, conversationReference, async (context, token) => { var message = MessageFactory.Text($"Sorry {mention.Text} cannot execute your code now. 😓"); message.Entities.Add(mention); await context.SendActivityAsync(message, token); }, cancellationToken); } } else if (string.IsNullOrWhiteSpace(DotNetInteractiveProcessRunner.Instance.SessionLanguage)) { var card = CardUtilities.CreateAdaptiveCardAttachment(CardJsonFiles.SelectLanguage); var attach = MessageFactory.Attachment(card); await turnContext.SendActivityAsync(attach, cancellationToken); } else if (content.Contains("👊")) { var mentioned = turnContext.Activity.GetMentions()?.FirstOrDefault(m => m.Mentioned.Id.EndsWith(_botId)); if (mentioned != null) { await turnContext.Adapter.ContinueConversationAsync(_botId, conversationReference, async (context, token) => { var message = MessageFactory.Text($"{mention.Text} back at you my friend! 👊"); message.Entities.Add(mention); await context.SendActivityAsync(message, token); }, cancellationToken); } } } else { var userAction = turnContext.Activity.Value; if (((JObject)userAction).Value <string>("userAction").Equals("SelectLanguage")) { if (string.IsNullOrWhiteSpace(DotNetInteractiveProcessRunner.Instance.SessionLanguage)) { var language = ((JObject)userAction).Value <string>("language"); DotNetInteractiveProcessRunner.Instance.SessionLanguage = language; var languageLabel = ((JObject)userAction).Value <string>("languageLabel"); var message = MessageFactory.Text($"All set. Let's write some {DotNetInteractiveProcessRunner.Instance.SessionLanguage} code together! 🤘🏻"); await turnContext.SendActivityAsync(message, cancellationToken); } } } }