Drop #300 (2023-07-21): Weekend Project Edition!

WebDev Chatbot Assistant CLI

We’re still going to “keep it simple” for the weekend project, but how “simple” is 100% up to you.

In yesterday’s Drop, we looked at the Mozilla Developer Network’s foray into “AI”. Keen-eyed readers might have suspected today’s Drop would have something to do with that, given the curl snippet I snuck into the section. If you were one of them that did, you were right!

Your Mission

space shuttle taking off

This WPE assignment is to build a basic CLI chatbot assistant — it can be a one-shot Q&A or a full-on chatbot — that works with the MDN AI Help API.

Since that is not an official API, we’re going to need a bit of help to do so.

Prerequisites

person writing on brown wooden table near white ceramic mug

Before we get started, you’ll need to sign up for MDN Plus.

The MDN Plus free tier gets you five questions per-day, and 5.00 USD/month gets you (what looks like) unlimited AI Help chat responses.

As I’ve noted before, the paid tier(s) are, IMO, well-worth the price of admission, but I grok that any extra outlay in this economy is not an option for everyone.

I’m also going to ask you to use Firefox to perform the login dance, since the example code I’m going to link to shows how to grab the precious auth-cookie from it, so you can use it in this CLI tool. You can grab cookies from other browsers, but I only use Firefox for MITM debugging and to store stuff like this, so the configuration is very pristine. I also suspect MDN treats Firefox users better.

The remainder of this Drop and some extra code I’m linking to assumes you have either done the above or know how to grab cookies from other browser cookie jars.

Interacting With The AI Helper Chat API

white robot toy holding black tablet

The core URL for the API is

https://developer.mozilla.org/api/v1/plus/ai/ask

and every request is expected to have an auth-cookie in the cookie headers.

It also expects to see a Content-Type of application/json headers. In Python (yes, Python; feel encouraged to raz me in the comments) the setup for that looks like this (assuming you’ve imported the requests module):

cookies = {
  'auth-cookie': auth_cookie,
}

headers = {
  'Accept': '*/*',
  'Content-Type': 'application/json',
}

There is a much nicer looking, full example of the below over in this supporting document, which also includes the bit about getting that auth_cookie.

The API expects questions (possibly with context from previous questions and answers) in this format:

question = {
  'messages': [
    {
      'role': 'user',
      'content': 'css center a div',
    },
  ],
}

If you want to add context, use your browser’s Developer Tools to see what that looks like. Just inspect all the /api/v1/plus/ai/ask requests to see how the web page adds that to the messages array.

You’ll make the actual request with a basic POST:

response = requests.post(
  url = 'https://developer.mozilla.org/api/v1/plus/ai/ask',
  cookies = cookies, 
  headers = headers, 
  json = question
)

The response.content value is an EventSource response, which are text messages in one big text blob that needs to be line split into an array. Each non-blank line is a JSON response that starts with a very-not-JSON data: string.

completions = [ line[6:].decode('utf-8') for line in response.content.splitlines() ]

completions = [ json.loads(completion) for completion in completions if completion != "" ]

You’ll need to inspect a few responses (trial/guessing/error are a big part of learning new things like this), but the first few ones for this prompt were:

[{'type': 'metadata',
  'sources': [{'url': '/en-US/docs/Learn/CSS/Howto/Center_an_item',
    'slug': 'Learn/CSS/Howto/Center_an_item',
    'title': 'How to center an item'},
   {'url': '/en-US/docs/Web/CSS/CSS_grid_layout/Box_alignment_in_grid_layout',
    'slug': 'Web/CSS/CSS_grid_layout/Box_alignment_in_grid_layout',
    'title': 'Box alignment in grid layout'},
   {'url': '/en-US/docs/Web/CSS/Layout_cookbook/Center_an_element',
    'slug': 'Web/CSS/Layout_cookbook/Center_an_element',
    'title': 'Center an element'},
   {'url': '/en-US/docs/Web/CSS/margin',
    'slug': 'Web/CSS/margin',
    'title': 'margin'}],
  'quota': None},
 {'id': 'chatcmpl-7egn1MGADelHsX2ddsLZrtHRHhFC0',
  'object': 'chat.completion.chunk',
  'created': 1689931959,
  'model': 'gpt-3.5-turbo-0613',
  'choices': [{'index': 0,
    'delta': {'content': '', 'role': 'assistant'},
    'finish_reason': None}],
  'usage': None},
 {'id': 'chatcmpl-7egn1MGADelHsX2ddsLZrtHRHhFC0',
  'object': 'chat.completion.chunk',
  'created': 1689931959,
  'model': 'gpt-3.5-turbo-0613',
  'choices': [{'index': 0,
    'delta': {'content': 'To', 'role': None},
    'finish_reason': None}],
  'usage': None},
 {'id': 'chatcmpl-7egn1MGADelHsX2ddsLZrtHRHhFC0',
  'object': 'chat.completion.chunk',
  'created': 1689931959,
  'model': 'gpt-3.5-turbo-0613',
  'choices': [{'index': 0,
    'delta': {'content': ' center', 'role': None},
    'finish_reason': None}],
  'usage': None},
]

You have some fun metadata with links, and some choices that contain the actual content (in delta). Note that it includes spaces and newlines (and, even, some markdown!).

FIN

The responses are probabilistic, even with such a constrained document corpus, so you’ll get slightly different responses each time. Some questions return just MDN links, too.

The code in this supporting document seems to auto-translate well (via your fav GPT LLM) to other languages. I used Python because it has nice EventSource streaming support (if you want this to appear more “typing back at you”) and, sadly, most folks know Python.

For the R folks out there, this {reticulate}s well, but you are welcome to use {curl} (I had some issues getting {httr} to work with the API).

With just a tiny bit of extra coding, you’ll have your very own CLI web development assistant! NOTE: You may need to re-up the cookie every so often, but that’s a quick browser action. ☮

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.