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

Teaching My Teleprompter to Listen

1 Share

I've long enjoyed using my open-source desktop teleprompter for recording videos and presentations. But there's been one feature I've wanted to add for years. Auto-scrolling. Not a fixed scroll speed. One that follows what I'm actually saying.

That's exactly what I added in the latest release: voice-driven auto-scroll. You talk, the script scrolls. No clickers, no foot pedals, no right-arrow key acrobatics. The goal was to make the scrolling disappear and not have to think about it again.

A bit of backstory: I originally built this teleprompter because every other one I tried hijacked my whole screen. I wanted a translucent overlay so I could keep eye contact with whoever I was talking to. Auto-scroll is the natural next step, letting the prompter follow my actual speaking pace.

Powered by Deepgram

Under the hood, the teleprompter streams your microphone audio to Deepgram for real-time speech-to-text. Every time Deepgram returns a chunk of text, it finds the closest matching spot in the script, and gently moves the text into position.

A few nerdy details that make the experience feel less janky:

  • Fuzzy alignment: A sliding-window multiset overlap (basically: "does the bag of words I just said look like the bag of words at script position X?"). It tolerates ad-libs, ums, re-reads, and Deepgram occasionally hearing "destroy" when you said "deploy."
  • Smooth motion: A critically-damped spring (Unity's classic SmoothDamp). Successive transcripts blend into one continuous glide instead of jumps.
  • Manual override: If you grab the trackpad or arrow keys, auto-scroll yields for a few seconds before picking back up. Backtrack to re-read a line without fighting the scroller.

Of course, if you're whispering or your mic is picking up the dishwasher, accuracy will tank. But for normal-volume reading in a reasonably quiet room, it works surprisingly well.

Privacy note: Auto-scroll only listens when you enable it. Audio is streamed to Deepgram for transcription, so don't use it with scripts you wouldn't be comfortable sending to a third-party speech-to-text service.

Setup takes about two minutes

Step 1: Update the app

Pull down the latest version (or build it yourself):

git clone https://github.com/reverentgeek/electron-teleprompter
cd electron-teleprompter
pnpm install
pnpm start

Step 2: Grab a Deepgram API key

Auto-scroll uses Deepgram for transcription. Sign up at console.deepgram.com. They have a free tier that's plenty for personal use.

Step 3: Tell the app about your key

In the menu, choose Script > Set Deepgram API Key… and paste your key into the modal.

Step 4: Enable auto-scroll

Open your script, and click Script > Toggle Auto-scroll (or press Cmd+Shift+L). A green "listening" pill appears in the top-right corner. You're ready to start reading!

Step 5: Tune to taste

The auto-scroll speed is adjustable so you can find your groove:

Shortcut Action
Cmd+] Increase scroll speed
Cmd+[ Decrease scroll speed
Cmd+\ Reset to 1×

Your speed setting (and your API key, and the microphone choice below) all persist across sessions.

Step 6: Pick your mic

If you've got a fancy USB lavalier, podcasting beast, or "this microphone costs more than my first car" setup plugged in, you'll want auto-scroll to use that, not your laptop's built-in array. Open Script > Select Microphone… and choose from the list.

Pro tip: Device names only show up after macOS/Windows has granted mic access at least once. If your dropdown looks like cryptic IDs the first time, start auto-scroll once to trigger the permission prompt, accept it, then reopen the picker and the names will be there.

If your saved mic is unavailable when auto-scroll starts — unplugged, you're not using the same dock, gremlins — the app falls back to the system default and flashes a quick "Saved mic unavailable — using default" hint. Your selection is preserved, so plugging the device back should work the next time.

What it feels like

I gotta say, the auto-scrolling is delightful and more than a little magical.

It's currently a little stop-and-go at times, based on pauses in speech, but it works! I can make mistakes or skip ahead, and it follows right along.

I'm super happy with it right now as it is. But, in the future, I want to experiment with Deepgram's "interim results" to make the scrolling experience even smoother.

But wait, there's more!

Here's a quick rundown on the features added since I introduced the teleprompter.

  • Writing scripts is easier: built-in markdown editor, syntax highlighting, save-in-place.
  • Reading is more comfortable: font size, opacity controls, mirror mode.
  • The app remembers things: recent files, last-opened script, window position, microphone choice.
  • The codebase is healthier: ESM, pnpm, and other under-the-hood cleanup.

Go talk to your teleprompter

I built this for myself, but I hope you find it useful, too! The code is open and the auto-scroll module is small and isolated. If you've got a better matching strategy (phonetic? embeddings? something I haven't thought of?), or you'd love to plug in a different STT provider, that's the place to start. PRs and weird ideas welcome over at github.com/reverentgeek/electron-teleprompter.

If you take it for a spin, I'd love to hear your feedback! What would make it more useful for you? What else should a desktop app be able to do once it can listen?

Read the whole story
alvinashcraft
14 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Advice I Like: Celebrate Success

1 Share

“On the way to a grand goal, celebrate the smallest victories as if each one were the final goal. That way, no matter where it ends, you are victorious.” – from Excellent Advice for Living

I believe in celebrating small things. I am a big “smell the roses” and “celebrate your success” even if the bigger part of the success feels like a failure. If I deliver a good demo in a talk, or one of my players makes a good hit/pass/etc., let’s celebrate that. If the rest of the talk wasn’t smooth and people are confused, or we kept hitting the ball in the net, that’s a bit of a failure and something to work on.

There’s always a bright side and a dim side. One is likely larger, but both are there.

I think if we look at something in black and white terms, and reduce it to success or failure, then you create a psychology that you win or something wasn’t worth doing. The sports teams, the musicians, the friends I have, my kids, we aren’t defined by whether we win every time. We can’t be the best all the time.

Even the best of the best isn’t the best most of the time. Tom Brady (7 rings) and Michael Jordan (6 rings) might be considered the best at what they did. However, they played more years (Brady 23, Jordan 15). Were they failures those other years?

The Beatles had 19 #1 albums, the most of all time. However, they released more. They had lots of #1 songs, but plenty that didn’t get to #1. Were those not worth doing?

Celebrate your success. You might write a great query, but ultimately there are plenty others that don’t perform well or some might even return the wrong results. Hopefully you’ll fix and improve those, but celebrate the things that go well.

Work on those that don’t and turn them into victories later.

 

I’ve been posting New Words on Fridays from a book I was reading, however, a friend thought they were a little depressing. They should be as they are obscure sorrows. I like them because they make me think.

To counter-balance those, I’m adding in thoughts on advice, mostly from Kevin Kelley’s book. You can read all these posts under the advice tag.

The post Advice I Like: Celebrate Success appeared first on SQLServerCentral.

Read the whole story
alvinashcraft
24 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Overcoming Imposter Syndrome: A Plan

1 Share

My most recent bout with imposter syndrome was with ISACA’s Digital Trust Ecosystem Framework (DTEF) foundation exam. I’m blogging about it because I know a lot of people struggle with imposter syndrome, even though they don’t specifically recognize what’s going on. So let’s talk about the challenge.

The DTEF foundation exam is a 60-question multiple choice exam that you have two hours to complete. Passing score is 65%. There’s not a lot of material. You can literally read through everything covered in a couple of days without straining. As exams go, it’s on the easier end of the spectrum.

Yet I was paralyzed when it came to taking it. As a matter of fact, the only reason I ended up taking it was because I ran out of time. I was on a special pathway towards getting trainers who were accredited to teach for the DTEF exam and accompanying certificate. From the time I registered to take the exam (after completing the accompanying on-line course), I had one year to do so. I could reschedule as much as I wanted to within that year, but I only had one year. I took the exam on the very last day after confirming with support that I couldn’t extend it any further.

As to my readiness, here’s my experience with the DTEF:

  • I am the regular columnist on Digital Trust for the ISACA Journal. I have been since 2023. I regularly refer to the DTEF when I write.
  • Because I was the regular columnist on Digital Trust, I received the framework when it was still in draft mode, back in 2022. ISACA keeps it current, so I’ve had access to the latest version since then.
  • I have taught a 20 hour course on the DTEF.
  • I have given other presentations about the DTEF.
  • I completed all the study materials for the DTEF.

I don’t know that there’s much more I could have done to be prepared for the exam. Yet I put it off and put it off and put it off for a whole year. I kept doubting my readiness and my ability to pass the exam. Basically, I was forced to either take the test or lose the attempt and that’s what forced me into action. When I actually sat for the test, I was prepared and passed easily. My imposter syndrome had caused this test to be a stressor in my life for a full year.

Unfortunately, though I am well-versed in what imposter syndrome is, I didn’t recognize it for what it was in this case. I was too close to the problem. I couldn’t detach. Actually, I didn’t even think to detach. Which leads me into some suggestions I’m going to try and do better at the next time I start to doubt myself. Maybe those will be of help to you.

  1. If I feel like I’m not capable of a task, I’m going to try and detach and look at it objectively.
  2. If that doesn’t work, I’m going to intentionally seek out someone I trust to talk the situation through.
  3. If that doesn’t work, I’m going to lean (pun intended) into something from Lean methodology: fail fast.

This doesn’t guarantee that I will overcome imposter syndrome in every future situation. However, it’s a plan with a clear trigger: I don’t feel capable. Perhaps I’m not. That’s what the first two steps are for: to try and identify if I am or am not. But there are a lot of times when we aren’t capable and we have to repeat the process over and over again. Back when I used to play soccer/football, I started out as a terrible goalkeeper. I couldn’t track the ball well, didn’t understand angles, didn’t know when to try and catch versus punch out, and I couldn’t time my jumps to properly intercept. Through practice I worked on all of those skills and got good enough to feel comfortable in goal for friendly and intramural games, which is what I wanted. It took a lot of failing to get there, though. And that’s what step 3 accomplishes.

The post Overcoming Imposter Syndrome: A Plan appeared first on SQLServerCentral.

Read the whole story
alvinashcraft
31 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Introducing langchain-azure-cosmosdb: Build Agentic Apps and RAG with One Database

1 Share

Build AI Agents and RAG Applications with the New LangChain + LangGraph Connector for Azure Cosmos DB

Building AI agents and RAG applications today means stitching together half a dozen services, a vector database, a chat history store, a checkpointer for agent state, a semantic cache, a long-term memory layer. Each adds operational overhead, latency, and technical debt.

langchain-azure-cosmosdb collapses that stack. It’s a Python LangChain and LangGraph connector that turns Azure Cosmos DB for NoSQL into the single persistence layer for all of your agentic app scenarios.

Azure Cosmos DB for NoSQL natively supports vector, full-text (lexical), and hybrid search, combined with high elasticity, automatic sharding, autoscale or serverless models, and up to a 99.999% SLA. It scales vector search from thousands to billions of vectors and is also the database powering scenarios across OpenAI including ChatGPT histories and memories. This integration makes it easier to build your agentic apps with one efficient, highly scalable source of truth for all your agentic data needs.

It’s available today on PyPI and Github, and can be easily installed via:

pip install langchain-azure-cosmosdb

null

Why a Dedicated Connector?

When building AI agents and RAG applications, developers often face a fragmented stack:

  • Vector search requires a separate vector database or service
  • Chat history needs another storage backend
  • Agent state checkpointing adds yet another system
  • Semantic caching means even more infrastructure
  • Long-term memory requires yet another bolt-on

This sprawl leads to complex integrations, higher operational costs, and a larger security surface. It also makes global distribution nearly impossible to achieve consistently across all these components.

The langchain-azure-cosmosdb package brings all of these capabilities directly into the LangChain and LangGraph ecosystem, so you can build your agentic apps on a single, highly scalable database, simplifying your app architecture and avoiding the need for specialized vector DBs or search engines.

What’s Included

The package ships with six integrations, each available in both synchronous and asynchronous variants:

Integration Sync Async What It Does
Vector Store AzureCosmosDBNoSqlVectorSearch AsyncAzureCosmosDBNoSqlVectorSearch Vector, full-text, hybrid, and weighted hybrid search
Semantic Cache AzureCosmosDBNoSqlSemanticCache AsyncAzureCosmosDBNoSqlSemanticCache Cache LLM responses to reduce latency and cost
Chat History CosmosDBChatMessageHistory AsyncCosmosDBChatMessageHistory Persist conversation history with TTL support
LangGraph Checkpointer CosmosDBSaverSync CosmosDBSaver Graph state persistence for multi-turn agents
LangGraph Cache CosmosDBCacheSync CosmosDBCache Node-level result caching for graph workflows
LangGraph Store CosmosDBStore AsyncCosmosDBStore Long-term memory with namespace organization and semantic search

Every integration supports both access key and Microsoft Entra ID (Managed Identity) authentication out of the box.

Semantic Search: Beyond Basic Search

Azure Cosmos DB’s vector search capabilities go well beyond basic similarity matching. The connector exposes all search modes:

  • Vector similarity search with DiskANN or Quantized Flat vector indexes for efficient similarity search at any scale
  • Full-text search with BM25 ranking
  • Hybrid search combining vector and full-text with RRF (Reciprocal Rank Fusion)
  • Weighted hybrid search for fine-tuned control over vector vs. text relevance

Here’s how to set up a vector store and run a hybrid search:

from azure.cosmos import CosmosClient, PartitionKey
from azure.identity import DefaultAzureCredential
from langchain_openai import AzureOpenAIEmbeddings
from langchain_azure_cosmosdb import AzureCosmosDBNoSqlVectorSearch


cosmos_client = CosmosClient(
    "<endpoint>",
    credential=DefaultAzureCredential(),
)

vectorstore = AzureCosmosDBNoSqlVectorSearch(
    cosmos_client=cosmos_client,
    embedding=AzureOpenAIEmbeddings(
        azure_endpoint="<openai-endpoint>",
        azure_deployment="text-embedding-3-small",
    ),
    vector_embedding_policy={
        "vectorEmbeddings": [
            {
                "path": "/embedding",
                "dataType": "float32",
                "distanceFunction": "cosine",
                "dimensions": 1536,
            }
        ]
    },
    indexing_policy={
        "vectorIndexes": [
            {
                "path": "/embedding",
                "type": "diskANN",
            }
        ],
        "fullTextIndexes": [
            {
                "path": "/text",
            }
        ],
    },
    cosmos_container_properties={
        "partition_key": PartitionKey(path="/id"),
    },
    cosmos_database_properties={
        "id": "my-rag-db",
    },
    vector_search_fields={
        "text_field": "text",
        "embedding_field": "embedding",
    },
    full_text_search_enabled=True,
)

# Add documents
vectorstore.add_texts(
    [
        "Azure Cosmos DB is a globally distributed database.",
    ]
)

# Hybrid search
results = vectorstore.similarity_search(
    "distributed database",
    k=5,
    search_type="hybrid",
    full_text_rank_filter=[
        {
            "search_field": "text",
            "search_text": "distributed",
        }
    ],
)

Building a Multi-Turn Agent with LangGraph

One of the most powerful use cases is building conversational agents that remember context across turns. With CosmosDBSaverSync, your LangGraph agent’s state is persisted to Cosmos DB automatically:

from langchain_openai import AzureChatOpenAI
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langchain_azure_cosmosdb import CosmosDBSaverSync


# Initialize LLM
llm = AzureChatOpenAI(
    azure_endpoint="<openai-endpoint>",
    azure_deployment="<chat-deployment>",
)

# Create checkpointer — falls back to DefaultAzureCredential if no key
checkpointer = CosmosDBSaverSync(
    database_name="agents-db",
    container_name="checkpoints",
    endpoint="<cosmos-endpoint>",
)


# Define state (simple message accumulator)
class State(dict):
    messages: list


# Define a simple chatbot graph
def chatbot(state):
    return {
        "messages": [
            llm.invoke(state["messages"]),
        ]
    }


graph = StateGraph(State)
graph.add_node("chatbot", chatbot)
graph.add_edge(START, "chatbot")
graph.add_edge("chatbot", END)

app = graph.compile(checkpointer=checkpointer)

# Multi-turn conversation — state persists across invocations
config = {
    "configurable": {
        "thread_id": "user-123",
    }
}

app.invoke(
    {
        "messages": [
            ("user", "Hi, I'm Alice!"),
        ]
    },
    config=config,
)

app.invoke(
    {
        "messages": [
            ("user", "What's my name?"),
        ]
    },
    config=config,
)

The checkpointer stores each graph step as a separate document in Cosmos DB, with support for get_state(), get_state_history(), and thread isolation.

Long-Term Memory with LangGraph Store

For agents that need to remember facts across sessions – user preferences, learned knowledge, extracted entities, the CosmosDBStore provides namespace-organized storage with optional semantic search:

from azure.identity import DefaultAzureCredential
from langchain_openai import AzureOpenAIEmbeddings
from langchain_azure_cosmosdb import CosmosDBStore


# Initialize embeddings
embeddings = AzureOpenAIEmbeddings(
    azure_endpoint="<openai-endpoint>",
    azure_deployment="text-embedding-3-small",
)

# Create store
store = CosmosDBStore.from_endpoint(
    endpoint="<cosmos-endpoint>",
    credential=DefaultAzureCredential(),
    database_name="agents-db",
    container_name="memory",
    index={
        "dims": 1536,
        "embed": embeddings,
        "fields": ["text"],
    },
)

store.setup()

# Store user preferences
store.put(
    ("users", "alice", "preferences"),
    "coffee",
    {
        "text": "Dark roast with oat milk",
    },
)

# Semantic search across all users
results = store.search(
    ("users",),
    query="beverage preferences",
    limit=5,
)

Semantic Caching: Reduce Costs and Latency

Identical or semantically similar prompts hitting your LLM repeatedly? The semantic cache stores responses and returns cached results for similar queries, dramatically reducing API costs and response times:

from langchain_core.globals import set_llm_cache

from langchain_azure_cosmosdb import (
    AzureCosmosDBNoSqlSemanticCache,
)


cache = AzureCosmosDBNoSqlSemanticCache(
    cosmos_client=cosmos_client,
    embedding=embeddings,
    vector_embedding_policy=vector_policy,
    indexing_policy=indexing_policy,
    cosmos_container_properties=container_props,
    cosmos_database_properties={
        "id": "cache-db",
    },
    vector_search_fields={
        "text_field": "text",
        "embedding_field": "embedding",
    },
    score_threshold=0.5,  # Configurable similarity threshold
)

set_llm_cache(cache)


# First call: ~3s (hits LLM)
llm.invoke("What is Azure Cosmos DB?")


# Second call: ~0.2s (cache hit)
llm.invoke("What is Azure Cosmos DB?")


# Similar prompt: ~0.2s (semantic cache hit)
llm.invoke("Describe Azure Cosmos DB briefly")

Full Async Support

Every integration has a native async counterpart using azure.cosmos.aio, so you can build high-throughput applications without blocking the event loop:

from langchain_azure_cosmosdb import CosmosDBSaver


async with CosmosDBSaver.from_conn_info(
    endpoint="<cosmos-endpoint>",
    key="<key>",
    database_name="agents-db",
    container_name="checkpoints",
) as checkpointer:
    app = graph.compile(checkpointer=checkpointer)

    result = await app.ainvoke(
        input,
        config=config,
    )

Enterprise-Ready from Day One

  • Microsoft Entra ID / Managed Identity: All integrations that create their own Cosmos client automatically fall back to DefaultAzureCredential when no key is provided — no secrets to manage.
  • Global Distribution: Cosmos DB’s multi-region writes and up to 99.999% SLA extends to your AI agent’s state, memory, and vector store.
  • DiskANN Indexing: Purpose-built for high-dimensional vector search at scale, delivering low-latency results even with millions of vectors.
  • User Agent Tracking: Every client connection includes a per-integration user agent string for usage tracking and diagnostics.

Get Started

Install the package:

pip install langchain-azure-cosmosdb

Explore the samples:

Explore the documentation:

We’d love to hear your feedback. Try it out, build something amazing, and let us know how it goes.

The post Introducing langchain-azure-cosmosdb: Build Agentic Apps and RAG with One Database appeared first on Azure Cosmos DB Blog.

Read the whole story
alvinashcraft
39 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Call For Papers Listings for 5/1

1 Share

A collection of upcoming CFPs (call for papers) from across the internet and around the world.

The post Call For Papers Listings for 5/1 appeared first on Leon Adato.

Read the whole story
alvinashcraft
1 minute ago
reply
Pennsylvania, USA
Share this story
Delete

Job listings for week ending 5/1

1 Share

Job postings that came across my desk, slack, email, discord, etc this week.

The post Job listings for week ending 5/1 appeared first on Leon Adato.

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