Friday, December 26, 2025

Black code formatting

 In the world of Python development, Black is known as "The Uncompromising Code Formatter." It is a tool that automatically reformats your Python code to ensure it adheres to a strict, consistent style.

Here is a breakdown of how it works and why it’s so effective for maintaining a codebase.

1. The "Determinism" Factor

Black is deterministic. This means that no matter what your code looks like before you run Black, it will always look exactly the same after.

 * Before: Three different developers might write the same function with different spacing, line breaks, or quote styles.

 * After: Black forces all three versions into one single, identical format.

2. Key Formatting Rules

Black makes several specific choices to keep code readable:

 * Line Length: It defaults to 88 characters per line (slightly more than the traditional PEP 8 limit of 79), which is often considered the "sweet spot" for modern monitors.

 * Quotes: It prefers double quotes (") over single quotes (') unless the string contains double quotes.

 * Trailing Commas: It uses trailing commas in collections (lists, dictionaries, etc.) to make "diffs" cleaner when you add new items.

 * Consistency: It handles nested expressions by breaking them into multiple lines in a very specific, predictable way.

3. Benefits for Teams

Using Black moves the focus away from "how the code looks" to "what the code does."

| Feature | Impact on Team |

|---|---|

| No Style Debates | Ends "bikeshedding" (useless arguments) in code reviews about tabs vs. spaces or quote styles. |

| Cleaner Diffs | Since formatting is automated, Git diffs only show actual logic changes, not "noise" from someone's IDE re-aligning spaces. |

| Speed | Developers don't have to manually format code while typing; they just write and let Black fix it on save. |

| Reduced Cognitive Load | When every file in a massive project looks the same, it's easier for a new developer to read and understand the logic. |

4. How to Use It

Black is typically used in one of three ways:

 * Command Line: Running black my_script.py in your terminal.

 * Editor Integration: Setting your IDE (VS Code, PyCharm) to "Format on Save."

 * CI/CD Pipelines: Setting up a GitHub Action to "fail" a pull request if the code hasn't been formatted with Black. This acts as a quality gate.

> The "Uncompromising" Philosophy: Black has very few configuration options. This is intentional. The goal isn't to let you customize your style—it's to give everyone the same style so no one has to think about it anymore.

Would you like me to show you how to set up a configuration file for Black, or perhaps how to integrate it into VS Code?


Thursday, December 25, 2025

What is connection pool pre ping sql alchemy

 While pool_pre_ping is a reactive "health check" performed just before a connection is used, pool_recycle is a proactive "retirement plan" for your database connections.

Setting pool_recycle ensures that no single connection stays open longer than a specified duration, preventing them from growing "stale" or being killed by external network layers.

1. The Problem: Silent Killers in the Cloud

Cloud providers like AWS RDS or Azure SQL use stateful firewalls and load balancers to manage traffic. These systems track every open connection. To save memory and port space, they will silently drop a connection from their "lookup table" if it hasn't shown activity for a while (often 30–60 minutes).

 * The SQLAlchemy perspective: It thinks the connection is still open.

 * The Cloud Provider perspective: It has already deleted the route.

 * The Result: When your AI service finally tries to send a query, the packet hits a "black hole," leading to a timeout or a hang.

2. How pool_recycle Solves This

By setting pool_recycle=1800 (30 minutes), you tell SQLAlchemy: "Even if this connection seems healthy, close it and open a fresh one if it has been alive for more than 1800 seconds."

This ensures the connection is rotated before the cloud provider's timeout (which is often set to 3600 seconds or 1 hour) has a chance to kick in.

3. Why 30 Minutes for an AI Service Layer?

In an AI-driven FastAPI backend, your workers are often performing "Long-Polling" or streaming responses (like ChatGPT-style typing).

 * Pre-Ping handles the "Zombie": It catches connections that are already dead.

 * Recycle handles the "Old": It prevents connections from reaching the age where they are likely to be killed.

Using both together creates a "Double Safety Net":

 * Recycle keeps the pool fresh so connections rarely die.

 * Pre-ping acts as a final check just in case one died anyway.

4. Full Implementation Example

Here is the production-ready configuration for a FastAPI AI service connecting to a cloud database:

from sqlalchemy import create_engine


# Configuration optimized for AWS RDS / Cloud SQL

engine = create_engine(

    SQLALCHEMY_DATABASE_URL,

    pool_pre_ping=True,      # Check health before use

    pool_recycle=1800,       # Retire connections every 30 mins

    pool_size=5,             # Keep 5 connections ready

    max_overflow=10,         # Allow 10 extra during traffic spikes

    connect_args={"connect_timeout": 10} # Don't wait forever to connect

)


Summary: Pre-Ping vs. Recycle

| Feature | pool_pre_ping | pool_recycle |

|---|---|---|

| Action | Test the connection before using it. | Replace the connection after X seconds. |

| Nature | Reactive (Fixes a problem). | Proactive (Prevents a problem). |

| Performance | Adds a tiny delay (SELECT 1) to every request. | Occurs only when a connection reaches its age limit. |

| Analogy | Checking if a lightbulb works before turning it on. | Replacing lightbulbs every 6 months so they don't burn out. |

Best Practice Tip for AI Services

If your AI inference takes a very long time (e.g., generating a 5-minute video or a massive data report), the connection might time out during the inference. In those specific cases, it is often better to close the DB session before starting the AI task and re-open a new one when you are ready to save the results.

Would you like to see how to structure a FastAPI background task that manages DB sessions this way to avoid timeouts during long AI processing?


Connection pool ping sql alchemy

 In the context of a FastAPI application serving an AI model, pool_pre_ping=True is a critical stability feature for your database engine (SQLAlchemy).

AI services often have high computational latency—the CPU or GPU is busy running inference while the database connection sits idle. This creates a specific set of challenges that pool_pre_ping solves.

1. Why Idle Connections Die

Most database servers (Postgres, MySQL) and network firewalls have a wait_timeout. If a connection isn't used for a certain period (e.g., 5 minutes), the server closes its end of the "pipe" to save resources.

However, the SQLAlchemy Connection Pool in your FastAPI worker doesn't know the connection is dead. It keeps the "zombie" connection in its pool.

2. The "First-Request" Failure in AI Services

In a typical AI service layer, your flow might look like this:

 * Request Received: FastAPI gets a prompt.

 * DB Check: Fetch user quotas or history from the DB.

 * Inference (The Gap): The system runs a heavy LLM or Computer Vision model for 10–30 seconds.

 * Result Storage: The system tries to save the AI response back to the DB.

If the "Inference" step takes too long, or if there is a long gap between user requests, the next time FastAPI tries to use a connection from the pool, the DB might have already closed it. Without pre-ping, your app tries to write data, hits a closed pipe, and crashes with an Internal Server Error (500).

3. How pool_pre_ping=True Works

This is often called a "Pessimistic Disconnect" strategy.

When your code asks for a connection (db = SessionLocal()), SQLAlchemy executes a tiny "heartbeat" query (like SELECT 1) before giving the connection to your app.

 * If it succeeds: The connection is healthy; the app continues.

 * If it fails: SQLAlchemy transparently drops that connection and creates a fresh one.

The user never sees an error.

4. Implementation in FastAPI

You set this at the Engine level in your database.py file:

from sqlalchemy import create_engine

from sqlalchemy.orm import sessionmaker


SQLALCHEMY_DATABASE_URL = "postgresql://user:password@localhost/dbname"


engine = create_engine(

    SQLALCHEMY_DATABASE_URL,

    # The magic flag

    pool_pre_ping=True, 

    # Recommended for AI services with long inference times

    pool_recycle=3600, 

    pool_size=10,

    max_overflow=20

)


SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)


Key Parameters for AI Services:

 * pool_pre_ping=True: Prevents "Broken Pipe" errors after idle periods.

 * pool_recycle: Periodically replaces connections regardless of health (e.g., every hour) to prevent memory leaks in the DB driver.

 * max_overflow: AI requests can be bursty; this allows the pool to temporarily grow if many users hit the AI model at once.

Comparison: With vs. Without Pre-Ping

| Scenario | Without pool_pre_ping | With pool_pre_ping |

|---|---|---|

| Idle Period | DB closes connection silently. | DB closes connection silently. |

| New Request | App uses "zombie" connection. | App tests connection first. |

| Outcome | Error: OperationalError (Broken Pipe). | Success: Connection refreshed. |

| Impact | User gets a 500 error; logs are messy. | Slight latency (ms) for the test query. |

When to use it?

You should always use this in production, especially if:

 * You use a managed database (like AWS RDS or Google Cloud SQL), which aggressively closes idle connections.

 * Your AI service has "bursty" traffic (busy for 10 minutes, then quiet for 1 hour).

 * Your inference logic takes significant time, allowing connections to time out during the request lifecycle.

Would you like to see how to wrap this into a FastAPI Dependency to ensure every AI request handles the database session safely?


Fastapi and graphana Prometheus monitoring

 To monitor a FastAPI application with Prometheus and Grafana, you typically follow a three-part architecture: the FastAPI App (generates metrics), Prometheus (scrapes and stores metrics), and Grafana (visualizes metrics).

The most efficient way to do this is using the prometheus-fastapi-instrumentator library, which automates the collection of standard metrics like request count, latency, and error rates.

Step 1: Instrument your FastAPI App

First, install the necessary library:

pip install prometheus-fastapi-instrumentator

Then, add these four lines to your main.py to expose a /metrics endpoint:

from fastapi import FastAPI

from prometheus_fastapi_instrumentator import Instrumentator


app = FastAPI()


# This exposes /metrics and starts tracking request data

Instrumentator().instrument(app).expose(app)


@app.get("/")

async def root():

    return {"message": "Monitoring is active!"}


Step 2: Configure Prometheus

Prometheus needs to know where to look for your app's data. Create a prometheus.yml file:

scrape_configs:

  - job_name: 'fastapi-app'

    scrape_interval: 5s

    static_configs:

      - targets: ['host.docker.internal:8000'] # Point this to your FastAPI server


Step 3: Run the Stack with Docker Compose

The easiest way to run everything together is using a docker-compose.yml file:

services:

  app:

    build: .

    ports:

      - "8000:8000"

  prometheus:

    image: prom/prometheus

    volumes:

      - ./prometheus.yml:/etc/prometheus/prometheus.yml

    ports:

      - "9090:9090"

  grafana:

    image: grafana/grafana

    ports:

      - [span_5](start_span)"3000:3000"[span_5](end_span)


Step 4: Visualize in Grafana

 * Open Grafana at http://localhost:3000 (Default login: admin / admin).

 * Go to Connections > Data Sources and add Prometheus. Use http://prometheus:9090 as the URL.

 * Go to Dashboards > New > Import.

 * Enter the ID 16110 (a popular community dashboard for FastAPI).

This dashboard will automatically populate with graphs showing your API's requests per second, p99 latency, and success rates.

Summary of Monitoring Components

| Component | Responsibility | Port |

|---|---|---|

| FastAPI | Generates raw metric data via /metrics. | 8000 |

| Prometheus | Pulls (scrapes) and stores time-series data. | 9090 |

| Grafana | Queries Prometheus to create visual dashboards. | 3000 |

Would you like me to show you how to create custom metrics (like tracking how many times a specific button is clicked) instead of just default request data?

Prometheus Metrics for your Python FastAPI App

This video provides a step-by-step walkthrough of setting up Prometheus metrics specifically for FastAPI using the instrumentator library.


YouTube video views will be stored in your YouTube History, and your data will be stored and used by YouTube according to its Terms of Service


SlowAPI for rate limiting

 SlowAPI is a Python library specifically designed to add rate limiting to FastAPI and Starlette applications. It is heavily inspired by (and based on) flask-limiter, adapting its battle-tested logic for the modern, asynchronous nature of FastAPI.

Its primary job is to prevent your API from being overwhelmed by too many requests—whether from a single user, a bot, or a malicious actor—by restricting how many times an endpoint can be called within a specific timeframe (e.g., "5 requests per minute").

Key Features

 * Decorator Support: Apply limits to specific routes using @limiter.limit().

 * Global Limits: Apply a default limit to every route in your application.

 * Flexible Identification: Rate limit based on IP address, headers, or even custom authentication tokens.

 * Storage Backends: Supports in-memory tracking (default), Redis, or Memcached for distributed systems.

Basic Example: FastAPI + SlowAPI

To use SlowAPI, you first need to install it:

pip install slowapi

Below is a complete implementation that limits a specific endpoint to 5 requests per minute per IP address.

from fastapi import FastAPI, Request

from slowapi import Limiter, _rate_limit_exceeded_handler

from slowapi.util import get_remote_address

from slowapi.errors import RateLimitExceeded


# 1. Initialize the Limiter

# 'key_func' determines how to identify the user (here, by their IP address)

limiter = Limiter(key_func=get_remote_address)


app = FastAPI()


# 2. Attach the limiter to the app state

app.state.limiter = limiter


# 3. Add the exception handler to return a 429 error when limits are hit

app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)


@app.get("/ping")

# 4. Apply the limit to a specific route

@limiter.limit("5/minute")

async def ping(request: Request):

    return {"message": "pong"}


@app.get("/unlimited")

async def unlimited(request: Request):

    return {"message": "I have no limits!"}


Important Usage Notes:

 * The Request object: Every route you want to rate limit must explicitly accept the request: Request argument, even if you don't use it in your code. SlowAPI needs this object to extract the client's identity (like the IP).

 * Order of Decorators: If you are using other decorators (like @app.get), ensure the @limiter.limit decorator is placed below the FastAPI route decorator.

Comparison: Rate Limiting Strategies

| Strategy | Description | Best For |

|---|---|---|

| Fixed Window | Resets at the start of every minute/hour. | Simple, predictable apps. |

| Sliding Window | Calculates usage over a rolling time window. | High-precision accuracy. |

| Token Bucket | Allows "bursts" of requests while maintaining an average rate. | APIs that expect occasional spikes. |

Would you like me to show you how to set up Global Rate Limits so you don't have to add a decorator to every single route?

Quick and Easy Rate Limiting for FastAPI

This video provides a practical walkthrough of setting up SlowAPI in a FastAPI project, including different strategies for managing traffic.


YouTube video views will be stored in your YouTube History, and your data will be stored and used by YouTube according to its Terms of Service


Langgraph thread model

 In LangGraph, Thread Models and Data Transfer Objects (DTOs) are fundamental concepts used to manage state, memory, and how information flows between different parts of your AI application.

While LangGraph is built on top of LangChain, it introduces specific ways to handle data to ensure "persistence" (saving the conversation) and "determinism" (making sure the graph behaves predictably).

1. Thread Model (Persistence and Memory)

In LangGraph, a Thread represents a unique session or a specific "run" of your graph. It is the mechanism that allows the agent to have a "memory" of previous interactions.

 * The Thread ID: Every conversation or task is assigned a thread_id. This ID acts as a key in a database (Checkpointer).

 * State Saving: After every node in the graph finishes executing, LangGraph automatically saves a "checkpoint" of the current state to that thread.

 * Resuming: If a user returns later and provides the same thread_id, the graph looks up the last checkpoint and resumes exactly where it left off.

 * Human-in-the-loop: Threads are what allow you to pause a graph, wait for a human to approve an action, and then resume it.

2. DTO (Data Transfer Objects)

While the term "DTO" is a general software engineering pattern, in the context of LangGraph, it refers to the Schema or State object that defines what data is passed between nodes.

In LangGraph, you define a TypedDict or a Pydantic model that acts as the single source of truth for the data moving through the graph.

Key Characteristics of the LangGraph "DTO":

 * The State Schema: It defines the fields (e.g., messages, documents, is_research_complete) that every node can read from or write to.

 * Reducers: This is a unique feature. When a node returns data, LangGraph uses "Reducers" to decide how to update the state. For example:

   * Overwrite: The new value replaces the old one.

   * Append: The new data (like a new chat message) is added to a list rather than replacing it.

Example Structure

from typing import Annotated, TypedDict

from langgraph.graph.message import add_messages


# This is your "DTO" or State Schema

class AgentState(TypedDict):

    # 'add_messages' is a reducer that appends new messages to the history

    messages: Annotated[list, add_messages] 

    status: str  # This will be overwritten by nodes


Comparison Summary

| Feature | Thread Model | DTO (State Schema) |

|---|---|---|

| Purpose | Handles History: Remembers who the user is and where they left off. | Handles Structure: Defines what information is being processed right now. |

| Storage | Managed by a Checkpointer (SQLite, Postgres, Redis). | Exists in memory during the execution of the graph. |

| Key Identifier | thread_id | Field names (e.g., messages, context). |

| Interaction | Allows for "Time Travel" (replaying old steps). | Governs how nodes communicate with each other. |

Would you like me to show you a code example of how to implement a persistent thread using a SQLite checkpointer?


Wednesday, December 17, 2025

What are different strategies for prompt Injection attacks?

 Here are 10 different strategies to mitigate prompt injection attacks, categorized by approach:


## **1. Input Sanitization & Validation**

- **Filter/escape user inputs**: Remove or encode special characters, delimiters, and command-like patterns

- **Allowlists/denylists**: Validate inputs against known safe patterns or block dangerous ones

- **Length limits**: Restrict input size to prevent overly complex injection attempts


## **2. Structural Separation**

- **Dual-prompt architecture**: Use separate "user prompt" and "system prompt" channels that never concatenate

- **Delimiter-based separation**: Use clear, unique delimiters and enforce parsing rules

- **Multi-stage processing**: Process untrusted input in isolation before incorporating into final prompt


## **3. Privilege Reduction**

- **Least privilege prompting**: Design system prompts with minimal permissions/capabilities

- **Sandboxed execution**: Run LLM calls in isolated environments with restricted API access

- **Output constraints**: Limit response formats (e.g., only JSON, no markdown, no code blocks)


## **4. Detection & Filtering**

- **Anomaly detection**: Monitor for unusual patterns in inputs (excessive special chars, repetition)

- **Classifier models**: Train or use secondary models to detect injection attempts

- **Pattern matching**: Check for known injection templates and attack signatures


## **5. Human-in-the-Loop**

- **Approval gates**: Critical actions require human confirmation

- **Selective grounding**: Only use pre-approved, verified information for sensitive tasks

- **Audit trails**: Log all prompts and responses for manual review


## **6. Post-Processing Validation**

- **Output sanitization**: Filter LLM responses before returning to users

- **Content verification**: Check outputs against expected formats/constraints

- **Secondary validation prompts**: Ask the LLM to verify its own response wasn't influenced by injection


## **7. Defense-in-Depth Prompting**

- **Instruction shielding**: Embed defensive instructions throughout the prompt

- **Negative examples**: Include examples of injections and explicitly reject them

- **Role reinforcement**: Constantly remind the LLM of its role and constraints


## **8. Architectural Controls**

- **API gateways**: Implement proxies that add security layers before reaching LLM

- **Rate limiting**: Prevent brute force attacks through request throttling

- **Request signing**: Ensure prompt integrity through cryptographic verification


## **9. Model-Level Defenses**

- **Fine-tuning against injections**: Train models to recognize and resist injections

- **Constitutional AI**: Implement model self-critique and principle-based constraints

- **Model-specific features**: Use vendor-specific protections (e.g., OpenAI's moderation endpoint)


## **10. Monitoring & Response**

- **Real-time alerting**: Trigger alerts on suspected injection attempts

- **Incident response plan**: Have procedures for investigating and mitigating successful attacks

- **Continuous testing**: Regularly test your system with new injection techniques


## **Best Practice Combination**

No single strategy is sufficient. A robust defense typically combines:

- **Prevention** (separation, sanitization)

- **Detection** (monitoring, classifiers)

- **Containment** (privilege reduction, sandboxing)

- **Response** (auditing, human review)


The most effective approach depends on your specific use case, risk tolerance, and whether you're building a consumer-facing application or internal tool. For high-risk applications, consider implementing at least 3-4 complementary strategies from different categories.