Sr. Content Developer at Microsoft, working remotely in PA, TechBash conference organizer, former Microsoft MVP, Husband, Dad and Geek.
156336 stories
·
33 followers

Creating Dual Use Windows GUI and Console Applications

1 Share
Building a tool that provides both a rich Windows GUI and a functional CLI from a single executable presents unique challenges due to how Windows handles subsystem types. This post explores three approaches for dual-mode apps: attaching to consoles from a GUI, launching UIs from a console app, and creating separate specialized EXEs. I’ll share the "clean as possible" workarounds for console jank and window flashing used in my own production tools.
Read the whole story
alvinashcraft
41 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Coffee and Open Source Conversation - Stacy Cashmore

1 Share
From: Isaac Levin
Duration: 1:12:10
Views: 2

Speaker, author, and software developer recognized with a Microsoft MVP Award for Developer Technologies. Stacy has been developing solutions since the mid-1990s across various industries. She authored Beginning Azure Static Web Apps and is passionate about Azure, DevOps, and empowering developers.

You can follow Stacy on Social Media
https://x.com/Stacy_Cash
https://www.stacy-clouds.net
https://nl.linkedin.com/in/stacycash
https://tech.lgbt/@stacyclouds
https://bsky.app/profile/stacy-clouds.net

PLEASE SUBSCRIBE TO THE PODCAST

- Spotify: http://isaacl.dev/podcast-spotify
- Apple Podcasts: http://isaacl.dev/podcast-apple
- Google Podcasts: http://isaacl.dev/podcast-google
- RSS: http://isaacl.dev/podcast-rss

You can check out more episodes of Coffee and Open Source on https://www.coffeeandopensource.com

Coffee and Open Source is hosted by Isaac Levin (https://twitter.com/isaacrlevin)

Read the whole story
alvinashcraft
42 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Design kits for iOS, iPadOS, and macOS 27 are here

1 Share
A collection of iOS UI components showcasing the Liquid Glass design language, displayed in dark mode, including a keyboard, tab bar, action menu, alert dialog, buttons, toggles, and sliders, arranged in an angled layout.

Apple design kits for Figma and Sketch are now available for iOS, iPadOS, and macOS 27. These include:

  • Updates to Liquid Glass
  • Expanded component and state support
  • Naming changes to better align with code
  • Improved resizing
  • The addition of Dark Mode for macOS

Download the design kits from the Apple Design Resources

Read the whole story
alvinashcraft
42 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Create SignFabric Envelopes from Mail Merge Templates in .NET C#

1 Share
This article explains how to use TX Text Control to mail merge JSON data into a document template and submit the merged document to the SignFabric API. It shows how your own application can create an envelope workflow by sending the document, signer metadata, and workflow options through the API.

Read the whole story
alvinashcraft
42 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Getting Started with NLP Using Hugging Face Transformers in Python

1 Share

Learn about natural language processing and explore the Hugging Face Transformers library in Python for tasks like text classification and summarization.

Natural Language Processing (NLP) focuses on enabling computers to understand, analyze and generate human language.

Modern NLP systems are largely powered by transformer architectures, which introduced the self-attention mechanism to model relationships between words regardless of their position in a sentence. This approach significantly improved performance on tasks such as text classification, summarization, translation and question answering, while also enabling models to capture long-range context more efficiently than earlier methods, such as recurrent neural networks (RNNs).

Popular transformer models include:

  • BERT (Bidirectional Encoder Representations from Transformers)
  • GPT (Generative Pre-trained Transformer)
  • RoBERTa
  • DistilBERT
  • T5

These models are pretrained on massive text datasets and can be fine-tuned for specific NLP tasks.

Hugging Face has become one of the most popular platforms for working with these models, providing easy-to-use tools, pretrained transformer models and Python libraries that allow developers to build powerful NLP applications with minimal code.

In this article, we will explore the basics of NLP using the Hugging Face Transformers library in Python, demonstrating how to implement common tasks such as text classification and text summarization using modern transformer models.

The Hugging Face Transformers library includes thousands of pretrained models that can be easily downloaded and used with just a few lines of Python code.

Key components of the Hugging Face ecosystem include:

  • Transformers library for pretrained models
  • A dataset library for loading NLP datasets
  • Tokenizers for efficient text processing
  • Hugging Face Hub for sharing models and datasets

The library supports both PyTorch and TensorFlow, making it flexible for different deep learning workflows.

Setting Up the Environment

In this tutorial, I assume you are new to Python. First, verify that Python is installed on your system. Then run the following commands to check the Python version and create or activate a virtual environment before installing the required dependencies.

  • python3 --version
  • python3 -m venv .venv
  • source .venv/bin/activate

After that, run the command to install transformers in the project.

  • pip3 install transformers torch

After that, add requirement.txt file in your project, and add entry:

pandas>=2.0.0

And then again run the command pip3 install -r requirements.txt.

At this point, you have installed all the required dependencies and set up the Python project environment. Now we can start building some NLP pipelines using the libraries we configured.

Text Classification

Text classification is one of the most common NLP tasks. It involves assigning predefined categories to text. Examples include sentiment analysis, spam detection and topic classification.

Using Hugging Face, we can create a text classification model in just a few lines of Python, as shown below.

from transformers import pipeline
import pandas as pd 

classifier = pipeline("text-classification")

output = classifier("I am excited for final cricket match")
df = pd.DataFrame(output)
print(df)

You should get the output shown below.

Label – positive. Score .99969

In this example, the pipeline automatically loads a pretrained transformer model that performs sentiment analysis. The model analyzes the text and predicts whether the sentiment is positive or negative.

Pipeline

The pipeline is a helper function, that performs three tasks.

  1. Preprocess – turn raw input into what model needs
  2. Run the model – run the model with given input
  3. Postprocess – turn model output into label, score or translated text

Preprocess – Running model – Postprocess

Essentially it takes a raw input and gives you clean output.

Named Entity Recognition

Using the pipeline wrapper, it is very easy to work with Named Entity Recognition (NER).

from transformers import pipeline
import pandas as pd
tags = pipeline("ner",aggregation_strategy="simple")

customer_feedback = "Barack Obama visited Tokyo in April 2014 and met with Shinzo Abe. The two leaders discussed trade agreements between the United States and Japan. Later, Obama gave a speech at the Roppongi Hills complex. The New York Times covered the event."
output = tags(customer_feedback)
df = pd.DataFrame(output)
print(df)

We are using Hugging Face NER pipeline to process feedback text, automatically identify named entities (such as people, locations and organizations). After converting the extracted entities into a tabular format using the pandas data frame=, you should get this output:

Table with entity group, score, word, start, end

In the example above, we use the NER pipeline to identify named entities in text, such as people, places and organizations.

The aggregation_strategy="simple" option is used to merge subword tokens back into complete entities. Without this setting, some models may split words into subword pieces (for example, “Obama” may appear as “Ob” and “##ama”). Using the “simple” aggregation strategy keeps these fragments combined and returned as a single entity span (such as “Obama”).

Text Summarization

Let’s try text summarization without using pipeline. We can do that in three steps as shown below:

from transformers import AutoTokenizer, AutoModelForSeq2SeqLM

model_name = "facebook/bart-large-cnn"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForSeq2SeqLM.from_pretrained(model_name)
text = """
 your text 
"""

inputs = tokenizer(
    text,
    return_tensors="pt",
    max_length=1024,
    truncation=True,
    padding=True,
)

outputs = model.generate(
    **inputs,
    max_length=150,
    min_length=30,
    num_beams=4,
    early_stopping=True,
)

summary = tokenizer.decode(outputs[0], skip_special_tokens=True)
print(summary)

The example above follows these steps:

  1. Loading the model
  2. Loading the tokenizer
  3. Tokenizing the input
  4. Generating the summary
  5. Decoding the output and printing

We are following all steps of preprocessing, running the model and the postprocessing as we are not using pipeline. This should give you an idea about pipeline utility function.

So, transformers are particularly powerful for NLP because they process text in parallel and capture context efficiently. The self-attention mechanism allows the model to determine which words in a sentence are most relevant when interpreting meaning.

For example, in the sentence:
“Apple released a new product.”
The word Apple could refer to a fruit or a technology company. A transformer model can use surrounding words like released and product to infer that the sentence refers to the company.

This contextual understanding is one of the major breakthroughs that transformer models introduced.

Transformer-based NLP systems are now used in many real-world applications:

  • Chatbots and virtual assistants
  • Automatic document summarization
  • Email spam filtering
  • Customer sentiment analysis
  • Search engines
  • Language translation
  • Code generation tools

Companies increasingly rely on transformer models to analyze large volumes of text data and automate tasks that previously required human interpretation.

I hope that you now have a solid understanding of transformers. Thanks for reading this article.

Read the whole story
alvinashcraft
42 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Handling Mutations with TanStack Query for Vue

1 Share

Learn how to handle mutations with TanStack Query in Vue to be able to send data back the server and keep the whole app in sync.

In my previous article on Data Fetching with TanStack Query for Vue, we went through all the things TanStack Query gives us for free when it comes to pulling data from an API: caching, automatic refetching on window focus, stale times, polling and a much cleaner template than the usual ref plus onMounted dance.

But of course, fetching data is only half of the story. At some point you’re going to need to actually send data back to the server. Creating users, updating records, deleting items, triggering actions. That’s where mutations come in!

In this article we’ll take a look at how to handle mutations with TanStack Query in Vue, how to hook into their lifecycle, how to keep the rest of your app in sync after something changes, and a little bit on optimistic updates.

What Is a Mutation?

In the context of TanStack Query, a mutation is any operation that changes server state. POST, PUT, PATCH, DELETE, basically anything that isn’t a GET. Queries fetch data; mutations change it.

The reason TanStack Query treats mutations as their own concept instead of just “another query” is because they behave very differently:

  • Queries run automatically when a component mounts or when their key changes.
  • Mutations are fired manually, usually as a reaction to a user action like submitting a form or clicking a button.
  • Queries are cached. Mutations are not.
  • After a mutation succeeds, other queries might now hold outdated data that we need to refresh.

We’ll address all of these, let’s start with the basics.

Your First Mutation with useMutation

The composable we’re going to use is useMutation, and the API is refreshingly simple. Let’s build a form that creates a new user.

CreateUser.vue

<script setup>
import { ref } from 'vue'
import { useMutation } from '@tanstack/vue-query'

const name = ref('')
const email = ref('')

const createUser = async (payload) => {
  const response = await fetch('https://myapp.com/users', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(payload),
  })

  if (!response.ok) {
    throw new Error('Failed to create user')
  }

  return response.json()
}

const { mutate, isPending, isError, error } = useMutation({
  mutationFn: createUser,
})

const onSubmit = () => {
  mutate({ name: name.value, email: email.value })
}
</script>

<template>
  <form @submit.prevent="onSubmit">
    <input v-model="name" placeholder="Name" />
    <input v-model="email" placeholder="Email" />
    <button :disabled="isPending" type="submit">
      {{ isPending ? 'Saving...' : 'Create user' }}
    </button>
    <p v-if="isError">Something went wrong: {{ error.message }}</p>
  </form>
</template>

Let’s break it down. useMutation takes a configuration object with one essential property:

  • mutationFn: The async function that actually performs the mutation. It must return a promise. If the promise rejects, TanStack Query considers the mutation failed.

Notice that unlike useQuery, there’s no mutationKey required here. Mutations can be given a key for advanced scenarios, but for most applications you’ll never need one.

The composable returns several reactive properties, and the ones you’ll end up using the most are:

  • mutate: The function you call to actually trigger the mutation. Any arguments you pass here are forwarded as the first parameter of your mutationFn.
  • isPending: A boolean that is true while the mutation is in flight. This one used to be called isLoading in older versions, so if you come across old examples online, that’s why.
  • isError / isSuccess: Booleans that reflect the final state of the last attempt.
  • error: Whatever error was thrown from the mutationFn if it failed.
  • data: The resolved value from the mutationFn if it succeeded.

I can’t stress enough how much cleaner this ends up being compared to writing your own try/catch plus a loading ref plus an error ref every single time you need a form submission.

mutate vs. mutateAsync

useMutation actually returns two different functions that you can use to trigger the mutation: mutate and mutateAsync. They do the same thing but in different ways.

  • mutate(variables) is fire and forget. It does not return a promise. Errors are swallowed by TanStack Query and surfaced through the reactive isError and error properties. You cannot await it.
  • mutateAsync(variables) returns a promise that you can await. If the mutation fails, the promise rejects, and you’re expected to handle the error yourself with a try/catch.

I normally use mutate most of the time because the reactive state is usually all I need. mutateAsync is really nice when you need to chain multiple operations together or when you’re inside a larger async flow and you want the mutation to behave like any other awaited call.

const { mutateAsync } = useMutation({ mutationFn: createUser })

const onSubmit = async () => {
  try {
    const user = await mutateAsync({ name: name.value })
    router.push(`/users/${user.id}`)
  } catch (err) {
    console.error('Could not create user', err)
  }
}

A word of caution: if you use mutateAsync and forget the try/catch, you’ll get an unhandled promise rejection warning in your console.

Lifecycle Callbacks

Let’s dive into the juicy bits of mutations now.

One of the things that makes useMutation so useful in real applications is the set of lifecycle callbacks it exposes. You can pass them either when you create the mutation or when you call mutate.

const { mutate } = useMutation({
  mutationFn: createUser,
  onSuccess: (data, variables) => {
    // data is what mutationFn returned
    console.log('User created!', data)
  },
  onError: (error, variables) => {
    console.error('Something broke', error)
  },
  onSettled: (data, error, variables) => {
    // Runs no matter what, after onSuccess or onError
  },
})

The three callbacks you’ll use the most are:

  • onSuccess: Fires when the mutation resolves successfully. Perfect for showing a toast, closing a modal, resetting a form or navigating away.
  • onError: Fires when the mutation rejects. Great for surfacing error messages to the user.
  • onSettled: Fires after either onSuccess or onError. Ideal for cleanup logic that needs to run regardless of outcome.

Keep in mind that you can also pass these callbacks directly to the mutate:

mutate(payload, {
  onSuccess: () => {
    showToast('Saved!')
    closeModal()
  },
})

If you define callbacks in both places, both will run. The ones on useMutation fire first, then the ones passed to mutate. Remember this if you end up seeing duplicate toasts or double navigations!

Normally you would use the mutate call callbacks whenever you need a specific call of the mutation to do something additional to what is already defined on the useMutation call.

Invalidating Queries After a Mutation

Here’s where mutations really start to shine, and where most of the real world value of TanStack Query lives.

Picture this scenario. You have a user list page that uses useQuery with the key ['users']. The user clicks a button to create a new user, the mutation fires, the backend responds happily with a 201, and… queue drumroll, the list on the screen still shows the old data. The cache has no idea anything changed.

The fix is to tell TanStack Query that the ['users'] query is now stale and should be refetched. We do that via the query client’s invalidateQueries method.

import { useMutation, useQueryClient } from '@tanstack/vue-query'

const queryClient = useQueryClient()

const { mutate } = useMutation({
  mutationFn: createUser,
  onSuccess: () => {
    queryClient.invalidateQueries({ queryKey: ['users'] })
  },
})

Any query with a matching key is marked stale and, if it is being observed by a mounted component, refetched immediately. If no component is currently observing it, it will simply be refetched the next time it gets mounted.

Pay close attention to what you set to the queryKey!

invalidateQueries({ queryKey: ['users'] }) will also invalidate ['users', 1], ['users', 2] and so on. This default behavior is usually what you will want for you applications, and you will end up setting up keys with specific words like users that you want to invalidate in groups.

If you only want to invalidate exact matches, you can pass exact: true.

queryClient.invalidateQueries({ queryKey: ['users'], exact: true })

I’ve found that 95% of the mutations I write end up following the exact same shape: do the thing, then invalidate the related list and/or detail queries. Once this clicks for you, you’ll start thinking of your data in terms of which queries each mutation affects.

Optimistic Updates

Sometimes waiting for the server feels sluggish. A user clicks “like” on a post, and the heart icon just sits there for 300ms while the request round trips. The fix for this is optimistic updates: update the UI as if the mutation already succeeded, then reconcile with the server afterward.

TanStack Query has first class support for this through the onMutate callback. The general idea goes like this:

  1. On onMutate, cancel any in flight queries for the related key, grab a snapshot of the current cached data, and write the optimistic value into the cache.
  2. Return the snapshot from onMutate so you can use it later.
  3. On onError, roll back to the snapshot.
  4. On onSettled, invalidate the query to make sure the real server data eventually wins.
const likePost = () => { someApiCall() }
const queryClient = useQueryClient()

const { mutate } = useMutation({
  mutationFn: likePost,
  onMutate: async (postId) => {
    await queryClient.cancelQueries({ queryKey: ['posts'] })

    const previousPosts = queryClient.getQueryData(['posts'])

    // We find the postId and add +1 the likes in the query data
    queryClient.setQueryData(['posts'], (old) =>
      old.map((p) => p.id === postId ? { ...p, likes: p.likes + 1 } : p)
    )

    // Don't forget to retun the modified data!
    return { previousPosts }
  },
  onError: (err, variables, context) => {
    // The `context` here is what we returned in onMutate
    queryClient.setQueryData(['posts'], context.previousPosts)
  },
  onSettled: () => {
    queryClient.invalidateQueries({ queryKey: ['posts'] })
  },
})

To be quite honest with you, optimistic updates are one of those features that sound wonderful on paper but that I only reach for when the UX genuinely demands it.

For the vast majority of forms and buttons in a typical CRUD app, the plain old “show a spinner, invalidate on success” flow is perfectly fine and a lot easier to reason about. Reach for optimistic updates when the snappiness actually matters, not by default.

Wrapping Up

Mutations are the missing half of the TanStack Query story, and once you pair them with invalidateQueries, the library really starts to pay for itself. You get a consistent way to handle every form, every button, every “do something on the server” action across your entire app. No more scattered loading refs, no more forgetting to refetch data after an update.

Today we covered the basics of useMutation, the difference between mutate and mutateAsync, the lifecycle callbacks, query invalidation and a quick look at optimistic updates. From here I’d recommend poking around the official mutations guide to see the more advanced patterns like mutation scopes and retry configuration.

Happy mutating!

Read the whole story
alvinashcraft
42 minutes ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories