Build A Multiblog WordPress CMS Website

Because of the effort and time required to create and scale up a new website, sometimes itโ€™s better to start small. There are many reasons to do this, including money, testing a concept, or simply wanting to start small. Then, as the blogs grow and take on their own identity, you can re-evaluate the strategy or migrate them to their website.

Whatever the case, a WordPress CMS blog gives you the flexibility to share multiple blogs on one site. It simplifies your blog by providing a preconfigured framework for adding content. This means that anyone can quickly launch a website without knowing HTML and CSS.

This strategy is being used for this website because I was unsure of what content type or how much I would write on each blog: Coding or French Fluency.

So, it made sense to start both blogs hosted on the same site. Then as they evolve and grow, evaluate them and make a decision about mid and long-term hosting. This is easy enough to do, but does require some consideration.

The Big Picture

By default, each WordPress site is configured to host one blog and one home page. But because of the platformโ€™s flexibility, multiple sites can be hosted with a few tweaks. Because WordPress does preconfigure a siteโ€™s framework, to host multiple sites, you only have to consider these key WordPress features to get started: widgets, menus, and pages.

To share two blogs on one site, you must define these three areas for each blog:

  • Widgets, such as sidebar widgets, contain information relevant to the site, such as recent posts, pages, and search.
  • Menus are used for navigation around your site. Itโ€™s how visitors find your content.
  • Pages are used to showcase your content, including posts, which are a special type of page.
Pages

Letโ€™s look at what WordPress features help you show your blog to visitors.

The first is that your blog post feed defaults to any blank page, which, once published and configured as your blog posts page, will display all posts published on your site. The name of the page doesnโ€™t matter; you can use any name, but it’s commonly named Blog.

When you specify this page as your blog posts page in Settings|Reading, anytime this page is loaded it will display all of your posts. As you create posts, they will appear on this page in reverse chronological order (newest to oldest).

This default option can no longer be used if your site hosts multiple pages.

Menus

Depending on your theme, WordPress allows you to create custom menus, and as such, there should be a set of menus per blog. The menu on the home page can be inclusive to let new visitors know whatโ€™s available. Still, ideally, once they navigate into the content for either blog, the menus should be blog-specific.

Widgets

Widgets are units of code that allow WordPress site owners to quickly display a list of pages, tags, and categories, to name a few. Each widget offers customizations such that you can control what content is visible. For example, a categories widget can be filtered to display specific categories or subcategories.

Keeping our focus on these key areas, weโ€™ll explore a strategy for hosting multiple blogs on one WordPress site.

A Simple Strategy

To configure a multi-blog WordPress website, the default configuration has to be bypassed so that your posts from each blog will appear separately. The simplest strategy is to use categories, pages, menus, and widgets. For this example, weโ€™ll use a scenario where you want to host two blogs on one WordPress CMS site.

Categories

Thinking about your content, consider what master categories you will use for each blog. There can be subcategories under each master, but having two master categories, one per blog, is key. On my site, the two master categories I created are โ€˜Codingโ€™ and โ€˜Franรงaisโ€™. When I publish a new post of each type, itโ€™s assigned to either category.

You can create subcategories under each main category, and they will also appear in the list of posts for the master category to which they belong. As you write posts, assign them to at least a master category. You donโ€™t need defined subcategories to separate the content for both blogs.

Then use these categories in blog-specific widgets for all relevant pages. To maintain the separation, all pages and links for each blog should be focused. That means creating a sidebar widget and menus for each blog.

Menus

Header Menu

Starting with the header menu, create custom header menu items, one for each blog. These additional menu items will be in addition to those you already have, such as a link to your home and about me pages. Starting with a unified header menu is the simplest option, as creating multiple header templates may require editing PHP code.

The goal is to ensure that once a visitor engages with one of the blogs, the header menu will allow them to navigate to your other blog(s) or the home page. Once a user navigates away from the home page, they will have this global header menu available.

For example, on my home page, the header menu bar contains a link to posts from my coding and French-language blogs. Clicking on each menu item takes you to a page that only contains posts for that category.

Widgets

The ease with which you can create new sidebars depends on your theme. Some themes are configured to allow multiple widget sidebars, while others are not. For my site, Iโ€™m using the Twenty Seventeen theme because I like its clean, simple design. However, it only allows one sidebar. To create multiple ones, I had to modify the themeโ€™s PHP code. If this is the case with your site, contact me, and Iโ€™ll try to provide assistance for your theme.

Pages

The third component to consider in our multi-blog strategy is Pages, which will require consideration and planning. Without the default Blog page, you will have to create two new pages, one for each blog, with a Latest Posts block to display posts by the master categories you established earlier. For each page, specify one master category to use, even if it has subcategories.

Any links you share for each blog should direct visitors to only the content you want them to see. With this strategy, there will be one home page, but since you can make it static, you can design the page to introduce both of your blogs. This strategy will eliminate the use of the default blog page configuration that WordPress expects in settings.

The home page should be configured as static in Settings>Reading. This will allow you to add any content you desire. For example, you could make the homepage be the home page for your priority business. Or, you could make the home page an introduction to both blogs.

If you create a new page, it should be specific for each blog and include the relevant widgets and links. Because pages canโ€™t be assigned to a category, you will have to manually populate it with relevant content. Be mindful that WordPress allows you to create pages similar to posts using blocks, so you don’t have to learn HTML or CSS.

On this site, I added a posts page for each blog and used the Latest Posts block to show recent posts. For my French blog, I created this page which contains my French language posts in reverse chronological order. For the coding blog, I created this one.

Posts

When I create a new post, I use add links to recent posts filtered by category at the end of each post. My site is fairly new so I expect that over time I can add more related content blocks to a post. This way, when a visitor wants to see more content from one of the blogs, they can use either the sidebar widgets or the latest posts content block. Of course, you can also include links to related content anywhere within the post, not just at the bottom or in the sidebar.

Specifying Sidebar Content

Each page and post for both blogs should contain topic-specific sidebar widgets, menus, and content. Depending on your theme, you may need additional plugins such as Content Aware Sidebars. Content Aware Sidebars lets you specify the content of existing sidebars. It doesn’t create them for you though, so if your theme doesn’t contain a sidebar, you’ll have to edit the theme.

Here you can see the sidebar content specified for posts and pages for each blog.

Then, within the Coding Page Sidebar, a condition and an action have been created.

Content Aware Sidebars configuration can be bit confusing at first, so the included screenshots should be helpful to understanding how to create a sidebar configuration. The plug-in does come with many more options, but it’s best to start simple then customize it as needed.

A Note of Caution

The amount of content that you can add to either blog depends on your hosting plan. If your plan allows a small amount of space but your blogs will contain video-rich content, then you may have to either upgrade your hosting plan or split your blogs into different sites sooner rather than later. If itโ€™s a text and image-based site, youโ€™ll have more time to develop your blogs before deciding to split them up.

Conclusion

Youโ€™ve seen one idea for how to separate multiple blogs on one WordPress site. The solution you create will most likely look different from mine because it depends on your theme and how far you push your customizations.

Whether technically savvy or not, the main goal is to control the sidebars, pages, posts and content that visitors will see. If you can devise a strategy for managing them you will be able to host multiple blogs with a reasonable amount of increased administrative overhead. Once you have these basic features configured, then you dig deeper into other parts of your blog such as your homepage and header menus.

Apprends Jeudi 04-24

Je ne sais pas pourquoi, mais les leรงons franรงaises sont un peu difficile aujourdโ€™hui. Jโ€™avais des problรจmes ร  รฉcouter et penser en franรงais. Alors, jโ€™ai quittรฉe tรดt. Peut รชtre jโ€™essayerai plus tard aujourdโ€™hui. Je nโ€™ai pas pu me concentrer.

Apprends Mercredi 04-23

Je prenais du temps ร  comprendre les expressions aujourdโ€™hui comme <lโ€™examen de demain>, <cโ€™est plus facile de + infinitif> et <Je la lui donne>. Ces expressions sont unique en franรงais. Je les trouvais un peu difficile ร  comprendre. Mais maintenant, je les comprends plus mieux.

ExpressionsNotes
Tu es prรชte pour <lโ€™examen de demain>?<lโ€™examen de demain> est le mรชme que <lโ€™examen demain>.
Peut รชtre <une panne gรฉnรฉrale>Quelque temp sans รฉlectricitรฉ.
Ce magasin est <sur l’avenue des Champs-ร‰lysรฉes> On dit <lโ€™avenue des Champs-รˆlysรฉes> parce que cโ€™est un lieu gรฉographique. Avec ces on doit utiliser lโ€™article dรฉfinit โ€˜lesโ€™ avec โ€˜deโ€™ pour obtenir โ€˜desโ€™. https://french.stackexchange.com/questions/51026/why-cant-lavenue-des-champs-ร‰lysรฉes-be-written-as-lavenue-de-champs-ร‰lysรฉe
Quelqu’un va descendreOu โ€˜quelquโ€™un descendโ€™.
Cโ€™est ร  un kilomรจtre dโ€™iciOn dit โ€˜ร  quelque distance (km, m, etc)โ€™ au lieu de seulement โ€˜quelque distanceโ€™.
Jโ€™ai conduit <partout> dans cette ville.
Je pouvais rarement sortirLe mรชme que โ€˜je sortais rarementโ€™
Avant, il n’y avait pas l’รฉlectricitรฉ chez nousโ€˜Pas lโ€˜รฉlectricitรฉโ€™ est il nโ€™a jamais dโ€™รฉlectricitรฉ. Mais โ€˜pas dโ€™รฉlectricitรฉโ€™ Est quand il y a dรฉjร  lโ€™รฉlectricitรฉโ€™ mais maintenant รงa ne marche pas.
Mais maintenant, cโ€™est plus facile de conduire une voiture ou de rester ร  la maison avec un ordinateur.On utilise la prรฉposition โ€˜deโ€™ avant dโ€™un verbe infinitif suivi dโ€™un complรฉment. Par exemple, il est facile ร  lireโ€™ contre โ€˜il est facile de lire ce livreโ€™.
Oui, je vais <tโ€™envoyer le lien>Ici, <le lien> est un URL.
Mon fils aime ce jeu alors <je le lui offrir>Voir en bas. Les prรฉnoms <lui> et <leur> suivent les prรฉnoms <le>, <la>, <les> avant le verbe
Si tu veux ce magazine, <je te le donne>La mรชme quโ€™avant
Je lui donne <la pomme>. Je <la lui> donne.Les prรฉnoms me, te, nous, et vous sont viennent avant le, la et les. Mais lui et leur sont aprรจs la, le et les. Cette diffรฉrence mโ€™est importante!
Chaque dimanche, jโ€™achรจte ce magazine hebdomadaireAcheter chaque semaine
Vous prรชtez vos livres ร  vos amisContre <on loue des livres dโ€™un bibliothรจque>.

Apprends Mardi 04-22

Jโ€™ai continuรฉ รฉtudier lโ€™imparfait est lโ€™orthographe des verbes imparfaits. Jโ€™ai comparรฉ le prรฉsent, le passรฉ composรฉ et lโ€™imparfait. Par exemple, le diffรฉrence entre โ€˜elle vaโ€™, โ€˜elle est allรฉeโ€™ et โ€˜elle allaitโ€™ pour le verbe โ€˜allerโ€™. Je comprends plus mieux aujourdโ€™hui, quโ€™avant, mais je veux apprendre lโ€™orthographe de tout.

FranรงaisLes notesAnglais
Avant, je ne lisais pas de livres numรฉriquesLes livres numรฉriques nโ€™existent pas.
Nous nโ€™avions pas de machine ร  laver quand jโ€™รฉtais petit. <Tu faisais comment> alors pour laver tes vรชtements?Comment on faisait nโ€™importe quoi.
Avant, je prenais rarement la voitureLโ€™adverbe โ€˜rarementโ€™ est aprรจs le verbe.
ร€ mon รฉpoque, <on prenait le temps>โ€˜On prenait le temps est une bonne expression ร  connaรฎtre.
Vous รฉtiez beaux ร  cette รฉpoque, et vous รชtes toujours trรจs beaux!
Tu as raison. <Je devrais> apprรฉcier la nature.<On devrait> mange moins sucre.
<Tout ร  coup>, Vikram entend un bruit dehorsQuelques chose qui se passe trรจs vite maintenant
Fais attention!
Il fait vraiment nuit!Il est dรฉjร  nuit
<Tu veux dire> โ€ฆ deux pizzas!Je veux dire โ€ฆ
dโ€™ร  cรดtรฉprรจs de quelque chose
rรฉduireFaire moins
Elle voyageait beaucoupLโ€™orthographe de โ€˜voyageaitโ€™ avec le โ€˜eโ€™ entre le โ€˜gโ€™ et โ€˜aโ€™.
Avant, cet auteur รฉcrivait ses livres avec un stylo et <cela prenait trop de temps>. Avec son nouvel ordinateur, il va vite et il est content.

Apprends Lundi 04-21

Jโ€™ai commencรฉ apprendre โ€˜lโ€™imparfaitโ€™. Par exemple, โ€˜รชtreโ€™ en imparfait est jโ€™รฉtais, tu รฉtais, il/elle รฉtait, nous รฉtions, vous รฉtiez, nous รฉtaient. Pour โ€˜avoirโ€™, cโ€™est jโ€™avais, tu avais, il/elle avait, nous avions, vous aviez, ils/elles avaient. Avec le -er verbe โ€˜allerโ€™ cโ€™est jโ€™allais, tu allais, il/elle allait, nous allions, vous alliez, ils/elles allaient.

FrenchEnglishNotes
<ร‡a te dit de> venir avec nous ?Tu dรฉsires ou tu veux. On peut dire aussi โ€˜que tu dit?โ€™ et โ€˜Comment environโ€™
<ร‡a vous dit de> venir avec nous ?
Il trouve toujours de lโ€™argent dans la rue. <Il a de la chance.>
<Nous allons louer> une maison pendant une semainepayer pour rester dans la maison
Je suis arrivรฉ <en avance> ร  la gare.avant lโ€™heure
Souhaiter quelque chose.je souhaite, tu souhaites, il/elle souhaite, vous souhaitons, ils/elles souhaitent
Nous <y sommes allรฉs> en trainNous <sommes allรฉs lร > en train
Alors, avez-vous passรฉ du temps <en Europe de lโ€™Est>?
Quand on <fait une randonnรฉe>, on se promรจne longtemps, en gรฉnรฉral ร  pied.Faire une randonnรฉe: โ€˜prendre une randonnรฉe; Faire de la randonnรฉe: โ€˜Je vais faire de la randonnรฉeโ€™
Jโ€™adore <skier>faire du ski vs skier: Jโ€™aime beaucoup skier. Je fais du ski bientรดt.
Vous connaissez <les Pyrรฉnรฉes>?
Je veux aller skier en France cet hiver. Dโ€™accord. ร‡a te dit dโ€™aller dans les Pyrรฉnรฉes?
Nous allons dans <les Alpes franรงaises>
Nous avons <un pneu crevรฉ>! Et nous nโ€™avons pas de <roue de secours>.un pneu sans lโ€™air;
Et tu <mโ€™as ouvert> la porte. Tu <mโ€™as surprise> avec des fleurs!En passรฉ composรฉ
On y est! Entrons.Cโ€™est รงa; ici allons y;
sentiersOn fait de la randonnรฉe sur sentiers.
Les vues sont incroyable du sommet
Il faut lui souhaiter bon chancevs Tu dois lui souhaiter bon chance
ร‡a prend
sourire
ร  mon รฉpoque, on faisait moins de vรฉlo que maintenantlโ€™imparfait:
Nous รฉtions jeunes et beaux
Nous avion une voiture jaune quand nous รฉtions plus jeunes
Ils pouvaient danser <pendant des heures> quand ils avaient vingt ans.
Avant, il faisaient la cuisine chez eux
La machine ร  laver et le micro-ondes sont <des appareil รฉlectriques> de la maison.
Quand jโ€™รฉtais petit, mes parent avaient seulement un appareil รฉlectrique chez eux.
ร€ mon รฉpoque, On faisait moins de vรฉlo que maintenant
des faitscomme: โ€˜le ciel est bleuโ€™ et โ€˜1 + 1 = 2โ€™

Apprends Dimanche 04-20

Jโ€™ai commencรฉ apprendre โ€˜lโ€™imparfaitโ€™. Par exemple, โ€˜รชtreโ€™ en imparfait est jโ€™รฉtais, tu รฉtais, il/elle รฉtait, nous รฉtions, vous รฉtiez, nous รฉtaient. Pour โ€˜avoirโ€™, cโ€™est jโ€™avais, tu avais, il/elle avait, nous avions, vous aviez, ils/elles avaient. Avec le -er verbe โ€˜allerโ€™ cโ€™est jโ€™allais, tu allais, il/elle allait, nous allions, vous alliez, ils/elles allaient.

FrenchEnglishNotes
<ร‡a te dit de> venir avec nous ?Tu dรฉsires ou tu veux. On peut dire aussi โ€˜que tu dit?โ€™ et โ€˜Comment environโ€™
<ร‡a vous dit de> venir avec nous ?
Il trouve toujours de lโ€™argent dans la rue. <Il a de la chance.>
<Nous allons louer> une maison pendant une semainepayer pour rester dans la maison
Je suis arrivรฉ <en avance> ร  la gare.avant lโ€™heure
Souhaiter quelque chose.je souhaite, tu souhaites, il/elle souhaite, vous souhaitons, ils/elles souhaitent
Nous <y sommes allรฉs> en trainNous <sommes allรฉs lร > en train
Alors, avez-vous passรฉ du temps <en Europe de lโ€™Est>?
Quand on <fait une randonnรฉe>, on se promรจne longtemps, en gรฉnรฉral ร  pied.Faire une randonnรฉe: โ€˜prendre une randonnรฉe; Faire de la randonnรฉe: โ€˜Je vais faire de la randonnรฉeโ€™
Jโ€™adore <skier>faire du ski vs skier: Jโ€™aime beaucoup skier. Je fais du ski bientรดt.
Vous connaissez <les Pyrรฉnรฉes>?
Je veux aller skier en France cet hiver. Dโ€™accord. ร‡a te dit dโ€™aller dans les Pyrรฉnรฉes?
Nous allons dans <les Alpes franรงaises>
Nous avons <un pneu crevรฉ>! Et nous nโ€™avons pas de <roue de secours>.un pneu sans lโ€™air;
Et tu <mโ€™as ouvert> la porte. Tu <mโ€™as surprise> avec des fleurs!En passรฉ composรฉ
On y est! Entrons.Cโ€™est รงa; ici allons y;
sentiersOn fait de la randonnรฉe sur sentiers.
Les vues sont incroyable du sommet
Il faut lui souhaiter bon chancevs Tu dois lui souhaiter bon chance
ร‡a prend
sourire
ร  mon รฉpoque, on faisait moins de vรฉlo que maintenantlโ€™imparfait:
Nous รฉtions jeunes et beaux
Nous avion une voiture jaune quand nous รฉtions plus jeunes
Ils pouvaient danser <pendant des heures> quand ils avaient vingt ans.
Avant, il faisaient la cuisine chez eux
La machine ร  laver et le micro-ondes sont <des appareil รฉlectriques> de la maison.
Quand jโ€™รฉtais petit, mes parent avaient seulement un appareil รฉlectrique chez eux.
ร€ mon รฉpoque, On faisait moins de vรฉlo que maintenant
des faitscomme: โ€˜le ciel est bleuโ€™ et โ€˜1 + 1 = 2โ€™

Google Gen AI 5-Day Intensive: Day Three (3/5)

Codelab #1

How to build an agent using LangGraph. Try code locally by downloading from Github or run it in a Kaggle notebook.

"""
Build an agent with LangGraph

Google Gen AI 5-Day Intensive Course
Host: Kaggle

Day: 3

Codelab: https://www.kaggle.com/code/markishere/day-3-building-an-agent-with-langgraph/#IMPORTANT!
"""

from pprint import pprint
from typing import Annotated, Literal

from google import genai
from IPython.display import Image
from langchain_core.messages.ai import AIMessage
from langchain_core.tools import tool
from langchain_google_genai import ChatGoogleGenerativeAI
from langgraph.graph import END, START, StateGraph
from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode
from typing_extensions import TypedDict
from collections.abc import Iterable
from random import randint
from langchain_core.messages.tool import ToolMessage

class OrderState(TypedDict):
    """State representing the customer's order conversation."""

    # Preserves the conversaton history between nodes.
    # The 'add messages' annotation indicates to LangGraphthat state
    # is updated by appending returned messages, not replacing them.
    messages: Annotated[list, add_messages]

    # The customer's in-progress order.
    order: list[str]

    # Flag indicating that the order is placed and completed.
    finished: bool


# The system instruction defines how the chatbot is expected to behave
# and includes rules for when to call different functions,
# as well as rules for the conversation, such as tone and what is permitted
# for discussion.
BARISTABOT_SYSINT = (
    # 'system' indicates the message is a system instruction.
    "system",
    "You are a BaristaBot, an interactive cafe ordering system. A human will talk to you about the "
    "available products you have and you will answer any questions about menu items (and only about "
    "menu items - no off-topic discussion, but you can chat about the products and their history). "
    "The customer will place an order for 1 or more items from the menu, which you will structure "
    "and send to the ordering system after confirming the order with the human. "
    "\n\n"
    "Add items to the customer's order with add_to_order, and reset the order with clear_order. "
    "To see the contents of the order so far, call get_order (this is shown to you, not the user) "
    "Always confirm_order with the user (double-check) before calling place_order. Calling confirm_order will "
    "display the order items to the user and returns their response to seeing the list. Their response may contain modifications. "
    "Always verify and respond with drink and modifier names from the MENU before adding them to the order. "
    "If you are unsure a drink or modifier matches those on the MENU, ask a question to clarify or redirect. "
    "You only have the modifiers listed on the menu. "
    "Once the customer has finished ordering items, Call confirm_order to ensure it is correct then make "
    "any necessary updates and then call place_order. Once place_order has returned, thank the user and "
    "say goodbye!"
    "\n\n"
    "If any of the tools are unavailable, you can break the fourth wall and tell the user that "
    "they have not implemented them yet and should keep reading to do so.",
)

# This is the message with which the system opens the conversation.
WELCOME_MSG = "Welcome to the BaristaBot cafe. Type `q` to quit. How may I serve you today?"

# Define chatbot node
# This node will represent a single turn in a chat conversation
#
# Try using different models. The Gemini 2.0 flash model is highly capable, great with tools,
# and has a generous free tier. If you try the older 1.5 models, note that the 'pro' models are
# better at complex multi-tool cases like this, but the 'flas' models are faster and have more
# free quota.
#
# Check out the features and quota differences here:
#  - https://ai.google.dev/gemini-api/docs/models/gemini
llm = ChatGoogleGenerativeAI(model="gemini-2.0-flash")


def chatbot(state: OrderState) -> OrderState:
    """The chatbot itself. A simple wrapper around the model's own chat interface."""
    message_history = [BARISTABOT_SYSINT] + state["messages"]
    return {"messages": [llm.invoke(message_history)]}


# Set up the initial graph based on our state definition.
graph_builder = StateGraph(OrderState)

# Add the chatbot function to the app graph as a node called 'chatbot'.
graph_builder.add_node("chatbot", chatbot)

# Define the chatbot node as the app entrypoint.
graph_builder.add_edge(START, "chatbot")

chat_graph = graph_builder.compile()

# Render the graph to visualize it.
Image(chat_graph.get_graph().draw_mermaid_png())


# The defined graph only as one node.
# So the chat will begin at __start__, execute the chatbot node and terminate.
user_msg = "Hello, what can you do?"
state = chat_graph.invoke({"messages": [user_msg]})

# The state object contains lots of informaton. Uncomment the pprint lines to see it all.
pprint(state)

# Note that the final state now has 2 messages. Our HumanMessage, and an additional AIMessage.
for msg in state["messages"]:
    print(f"{type(msg).__name__}: {msg.content}")


# Can be executed as Python loop
# Here it is manually invoked once
user_msg2 = "Oh great, what kinds of latte can you make?"

state["messages"].append(user_msg2)
state = chat_graph.invoke(state)

# pprint(state)
for msg in state["messages"]:
    print(f"{type(msg).__name__}: {msg.content}")


# Add a human node
# LangGraph can be looped between nodes
# This node will display the last message from the LLM to the user,
# then prompt them for their next input.
def human_node(state: OrderState) -> OrderState:
    """Display the last model message to the user and get the user's input."""
    last_msg = state["message"][-1]
    print("Model:", last_msg.content)

    user_input = input("User: ")

    # If it looks like the user is trying to quit, flag the conversaton as over.
    if user_input in {"q", "quit", "exit", "goodbye"}:
        state["finished"] = True

    return state | {"messages": [("user", user_input)]}


def chatbot_with_welcome_msg(state: OrderState) -> OrderState:
    """The chatbot itself. A wrapper around the model's own chat interface."""

    if state["messages"]:
        # If there are messages, continue the conversation with the Gemini model.
        new_output = llm.invoke([BARISTABOT_SYSINT]) + state["messages"]
    else:
        # If there are no messages, start with the welcome message.
        new_output = AIMessage(content=WELCOME_MSG)

    return state | {"messages": [new_output]}


# Start building a new graph.
graph_builder = StateGraph(OrderState)

# Add the chatbot and human nodes to the app graph.
graph_builder.add_node("chatbot", chatbot_with_welcome_msg)
graph_builder.add_node("human", human_node)

# Start with the chatbot again.
graph_builder.add_edge(START, "chatbot")

# The chatbot will always go to the human next.
graph_builder.add_edge("chatbot", "human")


# Create a conditional edge
def maybe_exit_human_node(state: OrderState) -> Literal["chatbot", "__end__"]:
    """Route to the chatbot, unless it looks like the user is exiting."""
    if state.get("finished", False):
        return END
    else:
        return "chatbot"


graph_builder.add_conditional_edges("human", maybe_exit_human_node)

chat_with_human_graph = graph_builder.compile()

Image(chat_with_human_graph.get_graph().draw_mermaid_png)


# The default recursion limit for traversing nodes is 25 - setting it higher means
# you can try a more complex order with multiple steps and round-trips and you can chat for longer!
config = {"recursion_limit": 100}

# Remember that this will loop forever, unless you input 'q', 'quit' or one of the other exit terms
# defined in 'human_node'.
# Uncomment this line to execute the graph:
# state = chat_with_human_graph.invoke({"messages": []}, config)
#
# Things to try:
#   - Just chat! There's no ordering or menu yet.
#   - 'q' to exit.

pprint(state)


# Add a "live" menu
# To create a dynamic menu to respond to changing stock levels
# There are two types of tools: stateless and stateful
# Stateless tools run automatically: get current menu: it doesn't make changes
# Stateful tools modify the order
# In LangGraph Python functions can be annotated as tools by applying @tools annotation
@tool
def get_menu() -> str:
    """Provide the latest up-to-date menu."""
    # Note that this is just hard-coded text, but you could connect this to a live stock
    # database, or you could use Gemini's multi-modal capabilities and take like photos
    # of your cafe's chalk menu or the products on the counter, and assemble them into an input.

    return """
        MENU:
        Coffee Drinks:
        Espresso
        Americano
        Cold Brew

        Coffee Drinks with Milk:
        Latte
        Cappuccino
        Cortado
        Macchiato
        Mocha
        Flat White

        Tea Drinks:
        English Breakfast Tea
        Green Tea
        Earl Grey

        Tea Drinks with Milk:
        Chai Latte
        Matcha Latte
        London Fog

        Other Drinks:
        Steamer
        Hot Chocolate

        Modifiers:
        Milk options: Whole, 2%, Oat, Almond, 2% Lactose Free; Default option: whole
        Espresso shots: Single, Double, Triple, Quadruple; default: Double
        Caffeine: Decaf, Regular; default: Regular
        Hot-Iced: Hot, Iced; Default: Hot
        Sweeteners (option to add one or more): vanilla sweetener, hazelnut sweetener, caramel sauce, chocolate sauce, sugar free vanilla sweetener
        Special requests: any reasonable modification that does not involve items not on the menu, for example: 'extra hot', 'one pump', 'half caff', 'extra foam', etc.

        "dirty" means add a shot of espresso to a drink that doesn't usually have it, like "Dirty Chai Latte".
        "Regular milk" is the same as 'whole milk'.
        "Sweetened" means add some regular sugar, not a sweetener.

        Soy milk has run out of stock today, so soy is not available.
      """


# Add the tool to the graph
# Define the tools and create a "tools" node.
tools = [get_menu]
tool_node = ToolNode(tools)

# Attach the tools to the model so that it knows what it can call.
llm_with_tools = llm.bind_tools(tools)


def maybe_route_to_tools(state: OrderState) -> Literal["tools", "human"]:
    """Route between human or tool nodes, depending if a tool call is made."""
    if not (msgs := state.get("messages", [])):
        raise ValueError(f"No messages found when parsing state: {state}")

    # Only route based on the last message.
    msg = msgs[-1]
    
    # When the chatbot returns tool_calls, rout to the "tools" node.
    if hasattr(msg, "tool_calls") and len(msg.tool_calls) > 0:
        return "tools"
    else:
        return "human"


def chatbot_with_tools(state: OrderState) -> OrderState:
    """The chatbot with tools. A simple wrapper around the model's own chat interface."""
    defaults = {"order": [], "finished": False}

    if state["messages"]:
        new_output = llm_with_tools.invoke(
            [BARISTABOT_SYSINT] + state["messages"]
        )
    else:
        new_output = AIMessage(content=WELCOME_MSG)

    # Set up some defaults if not already set, then pass through the provided state,
    # overriding only the "messages" field.
    return defaults | state | {"messages": [new_output]}


graph_builder = StateGraph(OrderState)

# Add the nodes, including the new tool_node.
graph_builder.add_node("chatbot", chatbot_with_tools)
graph_builder.add_node("human", human_node)
graph_builder.add_node("tools", tool_node)

# Chatbot may go to tools, or human.
graph_builder.add_conditional_edges("chatbot", maybe_route_to_tools)
# Human may go back to chatbot, or exit.
graph_builder.add_conditional_edges("human", maybe_exit_human_node)

# Tools always route back to chat afterwards.
graph_builder.add_edge("tools", "chatbot")

graph_builder.add_edge(START, "chatbot")
graph_with_menu = graph_builder.compile()

Image(graph_with_menu.get_graph().draw_mermaid_png())


# Remember that you have not implemented ordering yet, so this will loop forever,
# unless you input `q`, `quit` or one of the other exit terms defined in the
# `human_node`.
# Uncomment this line to execute the graph:
state = graph_with_menu.invoke({"messages": []}, config)

# Things to try:
# - I'd love an espresso drink, what have you got?
# - What teas do you have?
# - Can you do a long black? (this is on the menu as an "Americano" - see if it can
#   figure it out)
# - 'q' to exit.

pprint(state)


# Handle orders
# Update state to track an order and provide simple tools that update the state.
# You will need to be explicit as the model should not directly have access to
# the app's internal state.
#
# These functions have no body; LangGraph does not allow @tools to update the
# conversation state, so you will implement a separate node to handle state
# updates.

@tool
def add_to_order(drink: str, modifiers: Iterable[str]) -> str:
    """Adds the specified drink to the customer's order, including any modifiers.

    Returns:
        The updated order in progress.
    """

@tool
def confirm_order() -> str:
    """Asks customer if the order is correct.

    Returns:
        The user's free-text response.
    """

@tool
def get_order() -> str:
    """Returns the users order so far. One item per line."""


@tool
def clear_order():
    """Removes all items from the user's order."""


@tool
def place_order() -> int:
    """Sends the order to the barista for fulfillment.

    Returns:
      The estimated number of minutes until the order is ready.
    """

def order_node(state: OrderState) -> OrderState:
    """The ordering node. This is where the order state is manipulated."""
    tool_msg = state.get("messages", [])[-1]
    order = state.get("order", [])
    outbound_msgs = []
    order_placed = False

    for tool_call in tool_msg.tool_calls:
        if tool_call["name"] == "add_to_order":
            # Each order item is just a string. This is where it is assembled.
            # as "drink (modifiers, ...)".
            modifiers = tool_call["args"]["modifiers"]
            modifier_str = ", ".join(modifiers) if modifiers else "no modifiers"

            order.append(f"{tool_call["args"]["drink"]} ({modifier_str}))
            response = "\n".join(order)
        elif tool_call["name"] == "confirm_order":
            # We could entrust the LLM to do order confirmation, but it is a good practice to
            # show the user the exact data that comprises their order so that what they confirm
            # precisely matches the order that goes to the kitchen - avoiding hallucination
            # or reality skew.

            # In a real scenario, this is where you would connect your POS screen to show the
            # order to the user.

            print("Your order:")
            if not order:
                print(" (no items)")

            for drink in order:
                print(f" {drink}")

            response = input("Is this correct?")
        elif tool_call["name"] == "get_order":
            response = "\n".join(order) if order else "(no order)"
        elif tool_call["name"] == "clear_order":
            order.clear()
            response = None
        elif tool_call["name"] == "place_order":
            order_text = "\n".join(order)
            print("Sending order to kitchen!")
            print(order_text)

            # TODO: Implement cafe.
            order_placed = True
            response = randint(1, 5) # ETA in minutes
        else:
            raise NotImplementedError(f"Unknown tool call: {tool_call["name"]}")

        # Record the tool results as tool message.
        outbound_msgs.append(
            ToolMessage(
                content=response,
                name=tool_call["name"],
                tool_call_id=tool_call["id"]
            )
        )

    return {"messages": outbound_msgs, "order": order, "finished": order_placed}

def maybe_route_to_tools(state: OrderState) -> str:
    """Route between chat and tool nodes if a tool call is made."""
    if not (msgs := state.get("messages", [])):
        raise ValueError(f"No messages found when parsing state: {state}")

msg = msgs[-1]

if state.get("finished", False):
    # When an order is placed, exit the app. The system instruction indicates
    # that the chatbot should say thanks and goodbye at this point, so we can exit
    # cleanly.
    return END
elif hasattr(msg, "tool_calls") and len(msg.tool_calls) > 0:
    # Route to 'tools' node for any automated tool calls first.
    if any(
        tool["name"] in tool_node.tools_by_name,keys() for tool in msg.tool_calls
    ):
        return "tools"
    else:
        return "ordering"
    else:
        return "human"

# Define the graph so the LLM knows about the tools to invoke them.
# Auto-tools will be invoked automatically by the ToolNode
auto_tools = [get_menu]
tool_node = ToolNode(auto_tools)

# Order-tools will be handled by the order node.
order_tools = [add_to_order, confirm_order, get_order, clear_order, place_order]

# The LLM needs to know about all of the tools, so specify everything here
llm_with_tools = llm.bind_tools(auto_tools + order_tools)


graph_builder = StateGraph(OrderState)

# Nodes
graph_builder.add_node("chatbot", chatbot_with_tools)
graph_builder.add_node("human", human_node)
graph_builder.add_node("tools", tool_node)
graph_builder.add_node("ordering", order_node)

# Chatbot -> (ordering, tools, human, END)
graph_builder.add_conditional_edges("chatbot", maybe_route_to_tools)
# Human -> (chatbot, END)
graph_builder.add_conditional_edges("human", maybe_exit_human_node)

# Tools (both kinds) always route back to chat afterwards.
graph_builder.add_edge("tools", "chatbot")
graph_builder.add_edge("ordering", "chatbot")

graph_builder.add_edge(START, "chatbot")
graph_with_order_tools = graph_builder.compile()

Image(graph_with_order_tools.get_graph().draw_mermaid.png())


# Uncomment this line to execute the graph:
state = graph_with_order_tools.invoke({"messages": []}, config)

# Things to try:
# - Order a drink!
# - Make a change to your order.
# - "Which teas are from England?"
# - Note that the graph should naturally exit after placing an order.

pprint(state)

# Uncomment this once you have run the graph from the previous cell.
pprint(state["order"])
 

Apprends Samedi 04-19

Je veux penser, analyser et รฉcrire en franรงais. Je ne veux pas parle et pense en franรงais depuis รฉcrire en anglais. Alors, je le trouve plus facile ร  รฉcrire en franรงais le mรชme temp. Avant aujourdโ€™hui, jโ€™ai parlรฉ et pensรฉ en franรงais et analysรฉ en anglais. ร€ parler couramment, je dois analyser le franรงais dans franรงais.

FrenchEnglishNotes
Il neige beaucoup. Lโ€™avion ne peut pas partir, alors le vol est <annulรฉ>Annulรฉ est un adjectif: annulรฉe (fem. singulier), annulรฉs (masc. pluriel)
Je dรฉteste <la boxe>. Cโ€™est tellement ennuyeux.<la boxe> cโ€™est le sport.
Ce ne sont que des <hommes qui se battent> avec des gros <gants en cuir>Battre est un verbe se conjuguant avec Avoir est qui deux hommes fait ร  boxer.
Ils veulent juste gagner <un trophรฉe stupide>.Lโ€™adjectif โ€˜trophรฉe est masculin seulement.
Et aprรจs tu dis que la boxe, cโ€™est ennuyeux?!Cette phrase est amusante
Jโ€™en profite pour lire mon livreComme utiliser โ€˜enโ€™. โ€˜Enโ€™ peut remplacer les expressions comme โ€˜de la soupeโ€™. Et pour les quantitรฉs: โ€˜Jโ€™en ai six chaussures.โ€™
Quel est le problรจme? Il a oubliรฉ de <mettre ses vรชtements>.Par exemple: Je mets ma chemise.
Vous servez <les repas> dans les chambres?Le pluriel de โ€˜le repasโ€™. Le mot โ€˜repasโ€™ ne change pas en pluriel.
Ils sont allรฉs ร  Paris pour le weekend
Il nโ€™y a plus de pasElle mange tous les raisons et li nโ€™y a plus de pas pour moi.
Il <est probablement> en train de dormirLโ€™adverbe โ€˜probablementโ€™ est aprรจs le verbe quโ€™il modifie.
Tu as des timbres? Oui, jโ€™en ai dix.Utiliser โ€˜enโ€™ avec un quantitรฉ
Ma sล“ur <รฉconomise de lโ€™argent> et mon frรจre <en dรฉpense beaucoup>.Elle nโ€™aime pas dรฉpenser de lโ€™argent. Il nโ€™รฉconomise pas de lโ€™argent. โ€˜En dรฉpense beaucoup est comme dรฉpense trop.
Tu as beaucoup de chaussures parce que <tu en achรจtes trop>.โ€˜Tu en achรจtes tropโ€™: une bonne expression
Le portefeuilleQuโ€™on met de lโ€™argent
Oรน est mon sac ร  main?
Il ne faut pas trop dรฉpenser.Ne dรฉpense beaucoup dโ€™argent
Il a beaucoup dโ€™argent et <il aime en dรฉpenser>
Jโ€™en ai
Tu as <des piรจces de> deux euros ?
Tu as de lโ€™argent mais tu en dรฉpense seulement pour toi.
Ils ne dรฉpensent pas leur argent, alors ils ont beaucoup ร  la banque.

Apprends Vendredi 04-17

Jโ€™ai commencรฉ rรฉflรฉchir ร  la structure des phrases. Jโ€™ai pensรฉ ร  lโ€™ordre des pronoms avant le verbe actif. Si, il y a plus dโ€™un, je veux savoir lequel est plus proche le verbe actif. Maintenant, je sais que le pronom direct est avant le pronom indirect, les deux avant le verb. Cโ€™est important ร  savoir รงa.

FrenchEnglishNotes
Elle ne veut pas <<aucune question>>She doesnโ€™t want <<any questions>>Aucune is always followed by a singular noun
Cette phrase veut dire quoi?What does this sentence mean?This is a common saying to know
Nous avons des <<milliers des livres>>We have <<thousands of books>>
Il mโ€™demande lui envoyer un messageHe asked me to send him a messageHow to compose with direct and indirect pronouns
Tu me donnes quoi?What are you giving me?Also good to know
Je veux <<parler au directeur>> tout de suiteI want <<to talk to the director>> right away
Je viens de finir ma lettre. Super, je te donne une enveloppe tout de suite.I just finished my letter. Super, I am giving an envelope to you right now
Oรน est le bureau de la directrice?Where is the directorโ€™s office?
Vous <<รฉcrivez ร  la directrice>>You are writing to the director.
Antoine nโ€™est pas lร . Tu sais oรน il est? Non, attends. Je lui รฉcris un e-mail.Antoine isnโ€™t here. Do you know where he is? No, wait. I am writing an email to him.
Je suis fatiguรฉe et jโ€™ai besoin dโ€™un cafรฉ. Je <vais faire une pause>I am tired and I need a coffee. I <<am going to take a break>>.
Tu peux lui expliquer รงa au tรฉlรฉphoneYou can explain this by telephone to him(her).The word order
Je veux ce document <sur papier>. Jโ€™ai besoin dโ€™une imprimante.I want this document <<on paper>>. I need a printout.
Est-ce quโ€™<il y an encore du paper> dans lโ€™imprimante?Is there paper still in the printer.
Ces employรฉs sont nouveaux et je leur <montre les bureaux>.These employees are new and I am <<showing the offices to them>>.
Nous leurs expliquons <comment marche lโ€™imprimante>.We are explaining <<how the printer works>> to them.Sentence structure and word order
Hรฉlรจne, si tu ne connais pas mon voisin cรฉlรจbre, je peux <(te) le prรฉsenter>Helen, if you donโ€™t know my famous neighbor, I can <<introduce him (to you)>>.Pronouns appear in a specific order. The first one after the verb immediately precedes the verb with the second one further to the left of that pronoun
Cette employรฉe est nouvelle alors je lui prรฉsente les autres membres de lโ€™รฉquipeThis employee is new so Iโ€™m introducing the other team members to him.

Apprends Vendredi 04-16

Jโ€™ai commencรฉ rรฉflรฉchir ร  la structure des phrases. Jโ€™ai pensรฉ ร  lโ€™ordre des pronoms avant le verbe actif. Si, il y a plus dโ€™un, je veux savoir lequel est plus proche le verbe actif. Maintenant, je sais que le pronom direct est avant le pronom indirect, les deux avant le verb. Cโ€™est important ร  savoir รงa.

FrenchEnglishNotes
Elle ne veut pas <<aucune question>>She doesnโ€™t want <<any questions>>Aucune is always followed by a singular noun
Cette phrase veut dire quoi?What does this sentence mean?This is a common saying to know
Nous avons des <<milliers des livres>>We have <<thousands of books>>
Il mโ€™demande lui envoyer un messageHe asked me to send him a messageHow to compose with direct and indirect pronouns
Tu me donnes quoi?What are you giving me?Also good to know
Je veux <<parler au directeur>> tout de suiteI want <<to talk to the director>> right away
Je viens de finir ma lettre. Super, je te donne une enveloppe tout de suite.I just finished my letter. Super, I am giving an envelope to you right now
Oรน est le bureau de la directrice?Where is the directorโ€™s office?
Vous <<รฉcrivez ร  la directrice>>You are writing to the director.
Antoine nโ€™est pas lร . Tu sais oรน il est? Non, attends. Je lui รฉcris un e-mail.Antoine isnโ€™t here. Do you know where he is? No, wait. I am writing an email to him.
Je suis fatiguรฉe et jโ€™ai besoin dโ€™un cafรฉ. Je <vais faire une pause>I am tired and I need a coffee. I <<am going to take a break>>.
Tu peux lui expliquer รงa au tรฉlรฉphoneYou can explain this by telephone to him(her).The word order
Je veux ce document <sur papier>. Jโ€™ai besoin dโ€™une imprimante.I want this document <<on paper>>. I need a printout.
Est-ce quโ€™<il y an encore du paper> dans lโ€™imprimante?Is there paper still in the printer.
Ces employรฉs sont nouveaux et je leur <montre les bureaux>.These employees are new and I am <<showing the offices to them>>.
Nous leurs expliquons <comment marche lโ€™imprimante>.We are explaining <<how the printer works>> to them.Sentence structure and word order
Hรฉlรจne, si tu ne connais pas mon voisin cรฉlรจbre, je peux <(te) le prรฉsenter>Helen, if you donโ€™t know my famous neighbor, I can <<introduce him (to you)>>.Pronouns appear in a specific order. The first one after the verb immediately precedes the verb with the second one further to the left of that pronoun
Cette employรฉe est nouvelle alors je lui prรฉsente les autres membres de lโ€™รฉquipeThis employee is new so Iโ€™m introducing the other team members to him.