A Star Wars aside – Niggles far far away

I was very young when I first saw the trailer for Star Wars, with the TIE fighters attacking the Falcon. It’s fair to say I was hooked. Obviously the appeal to a 4-5 year old is a different than I have for some of the later films but I still connect with that early joy and I often rate a film by how much of an urge I have to jump off a sofa and re-enact some part of the story. But re-watching The Last Jedi it occurred to me that over the years there have been some irritating niggles that I hope future film makers can take note of.

  1. Stormtroopers can’t hit a barn door. There are plenty of times we see or hear about how precise and well trained the Stormtroopers are. But when it comes to shooting our heroes they can’t hit the side of barn door. Yes I know that’s true of lots of these shows; cowboys, A-Team, James Bond, etc. etc but it still annoys me
  2. R2D2 Rocket ship. You can’t assign great features or attributes to a character in the story timeline only for them not to exist later on. Dumb, stupid, dumb
  3. Slick CGI overly perfect ships. I suspect this will be an on-going theme, just like (2) you need to at least have a nod towards the timeline. Okay, so I can just about believe that in a time of peace the vehicles might all be in pristine condition but it just doesn’t work
  4. Abide by the story don’t spoil the mystery. Midi-Chlorians. No. You create a mystical aspect, leave it alone. We don’t need every last thing explained, allow us something to think about. Thankfully the recent set of films have conveniently ignored that tripe
  5. Vehicles with a purpose. This is a minor issue to me but a bit like (1) it just annoys me, always has. AT-ATs. What is the point of them? You’ve got vehicles that are happy in space but apparently it’s ‘too cold’ to defy gravity on Hoth? Riiiggghhht. Anyway, they are fun so I let this one go 😊
  6. Bombing run against the dreadnought. In space…bombs fall under gravity. Okay. I can just about swallow this for the drama, but only just
  7. Is it bird? It is a plane? No, it’s super Leia. See (4). Dreadful bit of The Last Jedi, awful, ruins it for me. Utter nonsense that serves no purpose to the narrative apart from saying, “Yes, I don’t care about the story universe. I’m gonna bust some holes”. Yeah, well done. Oh I forgot to ask how she gets back through the sealed door with a gaping hole into space. Yes, better not to ask. Just erase that whole section from memory
  8. The slow car chase in space – more story nonsense. Why wouldn’t the Imperial, sorry First Order, fleet just jump and surround them. Silly, but okay, drama – I get it
  9. The hard-disk library in Rogue One – where technology and story line clash. Okay, again for the drama, but come on, this was supposed to be a more adult film. It’s equivalent to all these rubbish shows were people just view contacts on a closed phone or desktop without ever needing a password. Yes it’s not real, but my suspension of disbelief can only be stretched so far
  10. Han & Chewie meet. Now, we know Chewie is strong, at least we hear a lot about. But the human body (I’m assuming Corellian equals Human) is pretty fragile. They’d be a puddle of skin and bones after that scene
  11. Flying reflexes. I love the speeder bikes on Endor. Dodging asteroids in Solo was fun but come on. You can just about forgive this in the context of mystical Jedi types, but those reflexes are ridiculous
  12. Hyper space jump attack. Oh dear, if this works then it would be a constant tactic (a problem that BSG also suffers from). Nope, no, nope, nah.
  13. Alliance ship-by-letter. Ok we get X-Wing, it makes sense. But then it all goes wrong. What is happening in the ship-building yards? Shall we build a new fighter based on a need? Nah, that’s too complicated, lets just just with a letter from an alphabet we don’t even use and create ships that loosely share the same shape as a letter. A, B, U, V, X, Y. Yes, that’s plausible 😉

There, that’s got that off my chest. By all means suspend my disbelief but keep the level consistent. Oh and if someone does a directors cut of The Last Jedi where Leia gets injured in the attack rather than coming from Krypton then please let me know.

Gotcha with Bot Framework AutoSaveStateMiddleware and “long” flows

The AutoSaveStateMiddleware is a great addition to a Bot project. On every turn it can save the BotState objects you configure it to look after. Takes a way a lot of noise and accidentally forgetting to call save yourself. However, this is a little gotcha with this mechanism. Consider a classic Pizza ordering scenario;

  • USER – What pizzas do you have?
  • BOT – We have Vegetarian, Pepperoni, Ham & Cheese, Ham & Mushroom

From a Bot Framework perspective you might implement that as a series of Waterfall steps – potentially using a 3rd party service, i.e. potentially unreliable – slow;

  • Gather Available Ingredients For Location Step, NextAsync
  • Gather Available Pizza Recipes for Available Ingredients Step, NextAsync
  • Show Available Pizza Step

So far so good. Now, let’s say that before we started this Waterfall off we provided the user with a ‘See our drinks menu’ hero card button. What we expect to happen is that the drinks button will be shown before the potentially longer running food menu is processed and shown to the user. But what happens if the user presses that drinks button before the food menu has run through its steps and displayed its results? Well, something not so pleasant is the answer. Since the AutoSaveState will fire on the Turn, it won’t have fired until it prompts the user for their pizza choice. Therefore, even though the Drink button is in the same overall flow, the same activity, when the message is received by the bot it will NOT have an Active Dialog set in its context. This means that the message will NOT be passed onto what we think is our active dialog.

What solutions are there?

  • You can manually call the Save State, but it’s sort of a pain because that’s why we’re using the AutoSaveState
  • Simply ignore this issue. You may lose messages but the user will just try again
  • Use a base class for your dialogs that overrides NextAsync and saves the Dialog state

Overcoming namespace clashes when upgrading to Bot Framework 4.3

V4.3 comes with some nice additional support that I was eager to use. However, there is a problem. v4.3 (Microsoft.Bot.Builder.Azure) uses the latest variant of the Azure Storage library whereas Microsoft.AspNetCore.All, via Microsoft.AspNetCore.DataProtection.AzureStorage (2.2.0), uses the older variant. This can cause problems if your own code wishes to use one of the clashing types. E.g. if you add


CloudStorageAccount blah = new CloudStorageAccount(null, false);

Then you’ll get an error like The type X exists in both Y and Z, e.g.


error CS0433: The type 'CloudStorageAccount' exists in both 'Microsoft.Azure.Storage.Common, Version=9.4.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' and 'Microsoft.WindowsAzure.Storage, Version=9.3.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'

The only solution I’ve found is to utilize the obscure extern and some trickery I grabbed from SO – using extern alias.

Step 1 – Create (or ensure there is) an xml file in the root of your project called Directory.Build.targets (don’t put .xml as the extension)

Step 2 – populate with;

  
<Project>
  <Target Name="AddPackageAliases" BeforeTargets="ResolveReferences" Outputs="%(PackageReference.Identity)">
    <PropertyGroup>
      <AliasPackageReference>@(PackageReference->'%(Identity)')</AliasPackageReference>
      <AliasName>@(PackageReference->'%(Alias)')</AliasName>
    </PropertyGroup>

    <ItemGroup>
      <ReferencePath Condition="'%(FileName)'=='$(AliasPackageReference)'">
        <Aliases>$(AliasName)</Aliases>
      </ReferencePath>
    </ItemGroup>
  </Target>
</Project>
  

Step 3 – edit the project. Unload your bot project, edit it and find the reference you wish to alias. Add Alias= E.g. to add the alias AzureCommon

<ItemGroup>
    <PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.9.1" />
    <PackageReference Include="Microsoft.ApplicationInsights.TraceListener" Version="2.9.1" />
    <PackageReference Include="Microsoft.AspNetCore" Version="2.2.0" />
    <PackageReference Include="Microsoft.AspNetCore.All" />
    <PackageReference Include="Microsoft.Azure.KeyVault.Core" Version="3.0.3" />
    <PackageReference Include="Microsoft.Azure.Storage.Common" Version="9.4.2" Alias="AzureCommon"/>

Save and reload the project

Step 4 (optional) -That should be enough to provide the separation for the compiler. But if you want to use the aliased version then add the extern to where you wish to use the clashing type, e.g.;


extern alias AzureCommon;
using AzureStore = AzureCommon;

...

AzureCommon.Microsoft.WindowsAzure.Storage.CloudStorageAccount blah =
new AzureCommon.Microsoft.WindowsAzure.Storage.CloudStorageAccount(null, false);

Step 5 – celebrate you’ve avoided this hiccup 🙂

Using the streamlined On handlers in Bot Framework v4.3

Bot Framework v4.3 has introduced a series of ‘On’ activity handlers to make you code more modular and easier to understand. Once you updated your project reference to use 4.3 you need to change you main bot to use the new activity handler class;

public class MayBotBot : IBot
To
 public class MayBotBot : ActivityHandler

You’ll then be able to use override to discover the options, e.g.


protected virtual Task OnMessageActivityAsync(ITurnContext turnContext, CancellationToken cancellationToken)
{
return Task.CompletedTask;
}

The other nice new feature that might slide under the radar is when the framework looks for a dialog Id it will now search up the stack to look for one. I.e. you could, if you wanted to, AddDialog all your possible dialogs in the root dialog and remove all AddDialog from everywhere else. Don’t do that, but in theory you could. The advantage here is that you can declare your common dialogs once and not have to keep adding them everywhere else.

Enjoy the chocolatey goodness of 4.3.

 

 

Upgrading your bot to .net core 2.2, warnings

When you upgrade your bot from .net core 2.0/2.1 to 2.2 you may see an warning that states; ‘A PackageReference to ‘Microsoft.AspNetCore.All’ specified a Version of 2.2.1. Specifying the version of this package is not recommended’.

To avoid this warning;

  1. Right click the bot project in the solution explorer and ‘unload’ it.
  2. Right click the unloaded project and edit it
  3. Change
    <PackageReference Include=”Microsoft.AspNetCore.All” Version=”2.2.1″/>
    To
    <PackageReference Include=”Microsoft.AspNetCore.All” />
  4. Reload the project.

 

Getting started with LUIS Containers

LUIS Containers is a really interesting (preview) feature. One of the issues with LUIS, and machine learning in general, is that each endpoint learns differently. E.g. if you create two LUIS endpoints, JANET and JOHN, and import the exact same JSON model then Janet and John will likely give you different scores for exactly the same utterance. Whilst that is just something you need to appreciate it, it does make a lot of workflow scenarios very difficult, E.g. testing. One potential solution to this is to export the LUIS model as a Container. In my initial tests this seems to clone the server, i.e. you get JANET2 rather than daughter of JANET – you get the same scores for the same utterance. Here is my add-on help to the documentation.

The main documentation is currently located at Install and run LUIS docker containers. If you don’t want to follow my guide then my advice is to read that but before you do anything read the next document along as that has a better explanation of the settings  Configure Language Understanding Docker containers

My guide to get up an running with LUIS Containers (for Windows 10)

I’m assuming you’ll be using the Command prompt and not a Bash terminal (as used in the official guide).

  1. Install Docker – if you’re new to Docker then I’d recommend at least following the first page of Get Started. Install Docker for Windows 10.
  2. Create a folder on your local disk that will container the LUIS model, e.g. C:\LUIS\Input and a folder for the output, e.g. C:\LUIS\Output
  3. Open the Docker settings panel (from the taskbar tools) and ensure you are sharing the local disk where you created the folders in (2)
  4. Go to LUIS and export the version of the model you want to use. Remember to select export as a Container.
  5. Get the Docker image for LUIS,
    docker pull mcr.microsoft.com/azure-cognitive-services/luis:latest
    
  6. Now create the container, remember this is all one line in CMD, so consider writing a batch file for it.
    docker run --rm -it -p 5000:5000 --memory 4g --cpus 2 --mount type=bind,src=c:\luis\input,target=/input --mount type=bind,src=c:\luis\output,target=/output mcr.microsoft.com/azure-cognitive-services/luis Eula=Accept Billing=https://YOUR_REGION.api.cognitive.microsoft.com/luis/v2.0 ApiKey=YOUR_API_KEY
    

    Where the Billing endpoint can be take from the first part of the Endpoint address in LUIS ‘Keys and Endpoint settings’ and the ApiKey is only the set of digits from the key of the same page. E.g. 3q919c439w2445f217b3w262622331c1

  7. You can then use your REST client of choice. You may use http://localhost:5000/swagger/index.html but be warned you’ll need to convert the AppId to a GUID (online coverter)…I know, right?? You should get an error saying something like; No model found with the given Application ID. If you do, then look at the output from your Docker console window. It should say something like could not find file xyz.gz.
  8. Copy the file you downloaded in (4) and put it into the input folder you created in (2). Carefully examine the name of the file. It needs to perfectly match the name of the file in the error message in (7)
  9. Ctrl+c the Docker container. Re-run the command from (6). Retry your REST call. You should now be working 🙂

NB, when you finish with this and try it again at a later date you may get errors when restarting your container, step (6). It may say something like, Error starting userland proxy. This appears to be a problem with Docker on Windows 10. You need to reopen the Docker desktop from the Taskbar and select Restart. This can take a little time but keep hovering over the icon and it will show you when it’s running again. Then you can re-issue step (6) and everything should be fine again.

One last note is that you need to stay online when using the Container. You can temporarily go offline but any prolonged absence and the LUIS Container will take itself offline with failed to reach metering endpoint,  resource temporarily unavailable. Shame, that scuppers offline use. Oh well, can’t have everything you want 🙂