diff --git a/.github/scripts/check_examples.sh b/.github/scripts/check_examples.sh new file mode 100755 index 000000000..4d2471131 --- /dev/null +++ b/.github/scripts/check_examples.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# Check that all example Python files are documented in examples/README.md + +EXAMPLES_DIR="examples" +README="examples/README.md" + +MISSING_README=() + +while IFS= read -r -d '' file; do + rel_path="${file#./}" + readme_path="/${rel_path}" + + if ! grep -qF "$readme_path" "$README"; then + MISSING_README+=("$readme_path") + fi +done < <(find "$EXAMPLES_DIR" -name "*.py" -print0 | sort -z) + +FAILED=0 + +if [ ${#MISSING_README[@]} -gt 0 ]; then + echo "ERROR: The following example files are not documented in $README:" + for f in "${MISSING_README[@]}"; do + echo " - $f" + done + echo "" + echo "Please add entries for these files to the table in $README." + FAILED=1 +fi + +if [ $FAILED -eq 1 ]; then + exit 1 +fi + +echo "All example files are documented in $README." diff --git a/.github/workflows/shared-packages.yml b/.github/workflows/shared-packages.yml index 0163fac4d..c1be55f46 100644 --- a/.github/workflows/shared-packages.yml +++ b/.github/workflows/shared-packages.yml @@ -81,6 +81,10 @@ jobs: run: | uv run ./check_licenses.sh + - name: Check all examples are documented in README + run: | + bash .github/scripts/check_examples.sh + - name: Check documentation builds correctly run: | uv run mkdocs build --strict diff --git a/examples/README.md b/examples/README.md index 296375295..1c8ab209a 100644 --- a/examples/README.md +++ b/examples/README.md @@ -12,40 +12,91 @@ All necessary details are provided in the comments at the top of each script. ## Scripts Overview -| Script | Ragbits Package | Description | -|:-------------------------------------------------------------------------------------------------|:------------------------------------------------------------:|:--------------------------------------------------------------------------------------------------------------------------------------------------------| -| [Text Prompt](/examples/core/prompt/text.py) | [ragbits-core](/packages/ragbits-core) | Example of how to use the `Prompt` class to generate themed text using an LLM with a simple text prompt. | -| [Text Prompt with Few Shots](/examples/core/prompt/text_with_few_shots.py) | [ragbits-core](/packages/ragbits-core) | Example of how to use the `Prompt` class to generate themed text using an LLM with a text prompt and few-shot examples. | -| [Multimodal Prompt with Image Input](/examples/core/prompt/multimodal_with_image.py) | [ragbits-core](/packages/ragbits-core) | Example of how to use the `Prompt` class to generate themed text using an LLM with both text and image inputs. | -| [Multimodal Prompt with PDF Input](/examples/core/prompt/multimodal_with_pdf.py) | [ragbits-core](/packages/ragbits-core) | Example of how to use the `Prompt` class to answer the question using an LLM with both text and PDF inputs. | -| [Multimodal Prompt with Few Shots](/examples/core/prompt/multimodal_with_few_shots.py) | [ragbits-core](/packages/ragbits-core) | Example of how to use the `Prompt` class to generate themed text using an LLM with multimodal inputs and few-shot examples. | -| [Tool Use with LLM](/examples/core/llms/tool_use.py) | [ragbits-core](/packages/ragbits-core) | Example of how to provide tools and return tool calls from LLM. | -| [Reasoning with LLM](/examples/core/llms/reasoning.py) | [ragbits-core](/packages/ragbits-core) | Example of how to use reasoning with LLM. | -| [OpenTelemetry Audit](/examples/core/audit/otel.py) | [ragbits-core](/packages/ragbits-core) | Example of how to collect traces and metrics using Ragbits audit module with OpenTelemetry. | -| [Logfire Audit](/examples/core/audit/logfire_.py) | [ragbits-core](/packages/ragbits-core) | Example of how to collect traces and metrics using Ragbits audit module with Logfire. | -| [Basic Document Search](/examples/document-search/basic.py) | [ragbits-document-search](/packages/ragbits-document-search) | Example of how to use the `DocumentSearch` class to search for documents with the `InMemoryVectorStore` class to store the embeddings. | -| [Chroma Document Search](/examples/document-search/chroma.py) | [ragbits-document-search](/packages/ragbits-document-search) | Example of how to use the `DocumentSearch` class to search for documents with the `ChromaVectorStore` class to store the embeddings. | -| [Multimodal Document Search](/examples/document-search/multimodal.py) | [ragbits-document-search](/packages/ragbits-document-search) | Example of how to use the `DocumentSearch` to index and search for images and text documents with the `MultimodalEmbedding` from VertexAI. | -| [PgVector Document Search](/examples/document-search/pgvector.py) | [ragbits-document-search](/packages/ragbits-document-search) | Example of how to use the `DocumentSearch` class to search for documents with the `PgVectorStore` class to store the embeddings in a Postgres database. | -| [Qdrant Document Search](/examples/document-search/qdrant.py) | [ragbits-document-search](/packages/ragbits-document-search) | Example of how to use the `DocumentSearch` class to search for documents with the `QdrantVectorStore` class to store the embeddings. | -| [Weaviate Document Search](/examples/document-search/weaviate_.py) | [ragbits-document-search](/packages/ragbits-document-search) | Example of how to use the `DocumentSearch` class to search for documents with the `WeaviateVectorStore` class to store the embeddings. | -| [Dataset Generation](/examples/evaluate/dataset-generator/generate.py) | [ragbits-evaluate](/packages/ragbits-evaluate) | Example of how to generate a synthetic dataset using the `DatasetGenerationPipeline` class. | -| [Basic Document Search Evaluation](/examples/evaluate/document-search/basic/evaluate.py) | [ragbits-evaluate](/packages/ragbits-evaluate) | Example of how to evaluate a basic document search pipeline using the `Evaluator` class. | -| [Basic Document Search Optimization](/examples/evaluate/document-search/basic/optimize.py) | [ragbits-evaluate](/packages/ragbits-evaluate) | Example of how to optimize a basic document search pipeline using the `Optimizer` class. | -| [Advanced Document Search Evaluation](/examples/evaluate/document-search/advanced/evaluate.py) | [ragbits-evaluate](/packages/ragbits-evaluate) | Example of how to evaluate an advanced document search pipeline using the `Evaluator` class. | -| [Advanced Document Search Optimization](/examples/evaluate/document-search/advanced/optimize.py) | [ragbits-evaluate](/packages/ragbits-evaluate) | Example of how to optimize an advanced document search pipeline using the `Optimizer` class. | -| [Chat Interface](/examples/chat/chat.py) | [ragbits-chat](/packages/ragbits-chat) | Example of how to use the `ChatInterface` to create a simple chat application. | -| [Offline Chat Interface](/examples/chat/offline_chat.py) | [ragbits-chat](/packages/ragbits-chat) | Example of how to use the `ChatInterface` to create a simple chat application that works offline. | -| [File Explorer Agent](/examples/chat/file_explorer_agent.py) | [ragbits-chat](/packages/ragbits-chat) | Secure file management agent with path validation and confirmation for all file operations within a restricted directory. | -| [Recontextualize Last Message](/examples/chat/recontextualize_message.py) | [ragbits-chat](/packages/ragbits-chat) | Example of how to use the `StandaloneMessageCompressor` compressor to recontextualize the last message in a conversation history. | -| [Agents Tool Use](/examples/agents/tool_use.py) | [ragbits-agents](/packages/ragbits-agents) | Example of how to use agent with tools. | -| [Agents OpenAI Native Tool Use](/examples/agents/openai_native_tool_use.py) | [ragbits-agents](/packages/ragbits-agents) | Example of how to use agent with OpenAI native tools. | -| [Agents Downstream Streaming](/examples/agents/downstream_agents_streaming.py) | [ragbits-agents](/packages/ragbits-agents) | Example of how to stream outputs from downstream agents in real time. | -| [Agents Hooks - Output Logging](/examples/agents/hooks/agent_output_logging.py) | [ragbits-agents](/packages/ragbits-agents) | Example of using POST_RUN hooks to log agent outputs. | -| [Agents Hooks - Guardrails](/examples/agents/hooks/guardrails_integration.py) | [ragbits-agents](/packages/ragbits-agents) | Example of using PRE_RUN hooks to integrate guardrails for input validation. | -| [Agents Hooks - Validation](/examples/agents/hooks/validation_and_sanitization.py) | [ragbits-agents](/packages/ragbits-agents) | Example of using PRE_TOOL and POST_TOOL hooks for argument validation and output sanitization. | -| [Agents CLI](/examples/agents/cli_agent.py) | [ragbits-agents](/packages/ragbits-agents) | Example of how to use agent in CLI. | -| [MCP Local](/examples/agents/mcp/local.py) | [ragbits-agents](/packages/ragbits-agents) | Example of how to use the `Agent` class to connect with a local MCP server. | -| [MCP SSE](/examples/agents/mcp/sse.py) | [ragbits-agents](/packages/ragbits-agents) | Example of how to use the `Agent` class to connect with a remote MCP server via SSE. | -| [MCP Streamable HTTP](/examples/agents/mcp/streamable_http.py) | [ragbits-agents](/packages/ragbits-agents) | Example of how to use the `Agent` class to connect with a remote MCP server via HTTP. | -| [A2A Orchestration](/examples/agents/a2a/run_orchestrator.py) | [ragbits-agents](/packages/ragbits-agents) | Example of how to setup A2A orchestration. | \ No newline at end of file +### Core (`examples/core/`) + +| Script | Ragbits Package | Description | +|:---------------------------------------------------------------------------------------|:-----------------------------------------------------:|:----------------------------------------------------------------------------------------------------------------------------------------| +| [Text Prompt](/examples/core/prompt/text.py) | [ragbits-core](/packages/ragbits-core) | Example of how to use the `Prompt` class to generate themed text using an LLM with a simple text prompt. | +| [Text Prompt with Few Shots](/examples/core/prompt/text_with_few_shots.py) | [ragbits-core](/packages/ragbits-core) | Example of how to use the `Prompt` class to generate themed text using an LLM with a text prompt and few-shot examples. | +| [Multimodal Prompt with Image Input](/examples/core/prompt/multimodal_with_image.py) | [ragbits-core](/packages/ragbits-core) | Example of how to use the `Prompt` class to generate themed text using an LLM with both text and image inputs. | +| [Multimodal Prompt with PDF Input](/examples/core/prompt/multimodal_with_pdf.py) | [ragbits-core](/packages/ragbits-core) | Example of how to use the `Prompt` class to answer the question using an LLM with both text and PDF inputs. | +| [Multimodal Prompt with Few Shots](/examples/core/prompt/multimodal_with_few_shots.py) | [ragbits-core](/packages/ragbits-core) | Example of how to use the `Prompt` class to generate themed text using an LLM with multimodal inputs and few-shot examples. | +| [Tool Use with LLM](/examples/core/llms/tool_use.py) | [ragbits-core](/packages/ragbits-core) | Example of how to provide tools and return tool calls from LLM. | +| [Reasoning with LLM](/examples/core/llms/reasoning.py) | [ragbits-core](/packages/ragbits-core) | Example of how to use reasoning with LLM. | +| [OpenTelemetry Audit](/examples/core/audit/otel.py) | [ragbits-core](/packages/ragbits-core) | Example of how to collect traces and metrics using Ragbits audit module with OpenTelemetry. | +| [Logfire Audit](/examples/core/audit/logfire_.py) | [ragbits-core](/packages/ragbits-core) | Example of how to collect traces and metrics using Ragbits audit module with Logfire. | + +### Document Search (`examples/document-search/`) + +| Script | Ragbits Package | Description | +|:-------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------:|:--------------------------------------------------------------------------------------------------------------------------------------------------------| +| [Basic Document Search](/examples/document-search/basic.py) | [ragbits-document-search](/packages/ragbits-document-search) | Example of how to use the `DocumentSearch` class to search for documents with the `InMemoryVectorStore` class to store the embeddings. | +| [Chroma Document Search](/examples/document-search/chroma.py) | [ragbits-document-search](/packages/ragbits-document-search) | Example of how to use the `DocumentSearch` class to search for documents with the `ChromaVectorStore` class to store the embeddings. | +| [Multimodal Document Search](/examples/document-search/multimodal.py) | [ragbits-document-search](/packages/ragbits-document-search) | Example of how to use the `DocumentSearch` to index and search for images and text documents with the `MultimodalEmbedding` from VertexAI. | +| [PgVector Document Search](/examples/document-search/pgvector.py) | [ragbits-document-search](/packages/ragbits-document-search) | Example of how to use the `DocumentSearch` class to search for documents with the `PgVectorStore` class to store the embeddings in a Postgres database. | +| [Qdrant Document Search](/examples/document-search/qdrant.py) | [ragbits-document-search](/packages/ragbits-document-search) | Example of how to use the `DocumentSearch` class to search for documents with the `QdrantVectorStore` class to store the embeddings. | +| [Weaviate Document Search](/examples/document-search/weaviate_.py) | [ragbits-document-search](/packages/ragbits-document-search) | Example of how to use the `DocumentSearch` class to search for documents with the `WeaviateVectorStore` class to store the embeddings. | + +### Evaluate (`examples/evaluate/`) + +| Script | Ragbits Package | Description | +|:-------------------------------------------------------------------------------------------------|:-----------------------------------------------------:|:---------------------------------------------------------------------------------------------------------| +| [Dataset Generation](/examples/evaluate/dataset-generator/generate.py) | [ragbits-evaluate](/packages/ragbits-evaluate) | Example of how to generate a synthetic dataset using the `DatasetGenerationPipeline` class. | +| [Basic Document Search Evaluation](/examples/evaluate/document-search/basic/evaluate.py) | [ragbits-evaluate](/packages/ragbits-evaluate) | Example of how to evaluate a basic document search pipeline using the `Evaluator` class. | +| [Basic Document Search Optimization](/examples/evaluate/document-search/basic/optimize.py) | [ragbits-evaluate](/packages/ragbits-evaluate) | Example of how to optimize a basic document search pipeline using the `Optimizer` class. | +| [Advanced Document Search Evaluation](/examples/evaluate/document-search/advanced/evaluate.py) | [ragbits-evaluate](/packages/ragbits-evaluate) | Example of how to evaluate an advanced document search pipeline using the `Evaluator` class. | +| [Advanced Document Search Optimization](/examples/evaluate/document-search/advanced/optimize.py) | [ragbits-evaluate](/packages/ragbits-evaluate) | Example of how to optimize an advanced document search pipeline using the `Optimizer` class. | + +### Chat (`examples/chat/`) + +| Script | Ragbits Package | Description | +|:----------------------------------------------------------------------------------------|:------------------------------------------------:|:---------------------------------------------------------------------------------------------------------------------------------| +| [Chat Interface](/examples/chat/chat.py) | [ragbits-chat](/packages/ragbits-chat) | Example of how to use the `ChatInterface` to create a simple chat application. | +| [Offline Chat Interface](/examples/chat/offline_chat.py) | [ragbits-chat](/packages/ragbits-chat) | Example of how to use the `ChatInterface` to create a simple chat application that works offline. | +| [Authenticated Chat Interface](/examples/chat/authenticated_chat.py) | [ragbits-chat](/packages/ragbits-chat) | Example of how to use the `ChatInterface` with user authentication (credentials, Discord and Google OAuth2). | +| [Chat Interface without Summary Generator](/examples/chat/no_summary_generator.py) | [ragbits-chat](/packages/ragbits-chat) | Example of how to use the `ChatInterface` without a `SummaryGenerator`. | +| [Recontextualize Last Message](/examples/chat/recontextualize_message.py) | [ragbits-chat](/packages/ragbits-chat) | Example of how to use the `StandaloneMessageCompressor` compressor to recontextualize the last message in a conversation history.| +| [Stream Events from Tools to Chat](/examples/chat/stream_events_from_tools_to_chat.py) | [ragbits-chat](/packages/ragbits-chat) | Example of how to stream custom tool events (e.g. charts) from an agent to the chat UI. | +| [Themed Chat Interface](/examples/chat/themed_chat.py) | [ragbits-chat](/packages/ragbits-chat) | Example of how to use the `ChatInterface` with a custom HeroUI theme. | +| [Tutorial Chat Interface](/examples/chat/tutorial.py) | [ragbits-chat](/packages/ragbits-chat) | Full-featured chat with authentication, user settings, feedback, live updates, web search and image generation. | +| [Upload Chat Interface](/examples/chat/upload_chat.py) | [ragbits-chat](/packages/ragbits-chat) | Example of how to use the `ChatInterface` with file upload support via `upload_handler`. | +| [File Explorer Agent](/examples/chat/file_explorer_agent.py) | [ragbits-chat](/packages/ragbits-chat) | Secure file management agent with path validation and confirmation for all file operations within a restricted directory. | + +### Agents (`examples/agents/`) + +| Script | Ragbits Package | Description | +|:-----------------------------------------------------------------------------------------------|:----------------------------------------------------:|:---------------------------------------------------------------------------------------------------------| +| [Tool Use](/examples/agents/tool_use.py) | [ragbits-agents](/packages/ragbits-agents) | Example of how to use agent with tools. | +| [Agent with Decorator](/examples/agents/with_decorator.py) | [ragbits-agents](/packages/ragbits-agents) | Example of how to use the `@Agent.prompt_config` decorator to define an agent as a class. | +| [Agent Dependencies](/examples/agents/dependencies.py) | [ragbits-agents](/packages/ragbits-agents) | Example of how to bind dependencies to an agent via `AgentRunContext`. | +| [OpenAI Native Tool Use](/examples/agents/openai_native_tool_use.py) | [ragbits-agents](/packages/ragbits-agents) | Example of how to use agent with OpenAI native tools. | +| [Downstream Agent Streaming](/examples/agents/downstream_agents_streaming.py) | [ragbits-agents](/packages/ragbits-agents) | Example of how to stream outputs from downstream agents in real time. | +| [Streaming Events from Tools](/examples/agents/stream_events_from_tools.py) | [ragbits-agents](/packages/ragbits-agents) | Example of how to define a tool that emits custom events that can be streamed from an agent. | +| [CLI Agent](/examples/agents/cli_agent.py) | [ragbits-agents](/packages/ragbits-agents) | Example of how to expose an agent via the ragbits CLI for interactive use. | + +#### Hooks (`examples/agents/hooks/`) + +| Script | Ragbits Package | Description | +|:---------------------------------------------------------------------------------------------|:----------------------------------------------------:|:---------------------------------------------------------------------------------------------------------| +| [Output Logging](/examples/agents/hooks/agent_output_logging.py) | [ragbits-agents](/packages/ragbits-agents) | Example of using POST_TOOL hooks to log outputs returned by downstream agent tools. | +| [Guardrails Integration](/examples/agents/hooks/guardrails_integration.py) | [ragbits-agents](/packages/ragbits-agents) | Example of using PRE_RUN hooks for guardrail-based input validation and ON_EVENT hooks for streaming. | +| [Validation and Sanitization](/examples/agents/hooks/validation_and_sanitization.py) | [ragbits-agents](/packages/ragbits-agents) | Example of using PRE_TOOL and POST_TOOL hooks for argument validation, sanitization, and output masking. | + +#### MCP (`examples/agents/mcp/`) + +| Script | Ragbits Package | Description | +|:--------------------------------------------------------------------------|:----------------------------------------------------:|:----------------------------------------------------------------------------------------| +| [MCP Local](/examples/agents/mcp/local.py) | [ragbits-agents](/packages/ragbits-agents) | Example of how to use the `Agent` class to connect with a local MCP server. | +| [MCP SSE](/examples/agents/mcp/sse.py) | [ragbits-agents](/packages/ragbits-agents) | Example of how to use the `Agent` class to connect with a remote MCP server via SSE. | +| [MCP Streamable HTTP](/examples/agents/mcp/streamable_http.py) | [ragbits-agents](/packages/ragbits-agents) | Example of how to use the `Agent` class to connect with a remote MCP server via HTTP. | + +#### A2A (`examples/agents/a2a/`) + +| Script | Ragbits Package | Description | +|:----------------------------------------------------------------------------------------------------------|:----------------------------------------------------:|:------------------------------------------------------------------------------------------------------------------------| +| [Flight Agent](/examples/agents/a2a/flight_agent.py) | [ragbits-agents](/packages/ragbits-agents) | A2A-compatible agent that returns available flights between two cities; used as a remote sub-agent in orchestration examples. | +| [Hotel Agent](/examples/agents/a2a/hotel_agent.py) | [ragbits-agents](/packages/ragbits-agents) | A2A-compatible agent that recommends hotels for a given city; used as a remote sub-agent in orchestration examples. | +| [City Explorer Agent](/examples/agents/a2a/city_explorer_agent.py) | [ragbits-agents](/packages/ragbits-agents) | A2A-compatible agent that fetches and summarizes city information from Wikipedia using an MCP fetch server. | +| [Agent Orchestrator](/examples/agents/a2a/agent_orchestrator.py) | [ragbits-agents](/packages/ragbits-agents) | A custom `AgentOrchestrator` class that coordinates multiple remote A2A agents; imported by the orchestration examples. | +| [A2A Orchestration](/examples/agents/a2a/run_orchestrator.py) | [ragbits-agents](/packages/ragbits-agents) | Example of how to set up an A2A orchestration using a custom `AgentOrchestrator` class that routes tasks to remote agents. | +| [A2A Orchestration with Tools](/examples/agents/a2a/agent_orchestrator_with_tools.py) | [ragbits-agents](/packages/ragbits-agents) | Example of how to set up an A2A orchestrator that routes tasks to remote agents using tools, with streamed output. | diff --git a/examples/agents/cli_agent.py b/examples/agents/cli_agent.py index 05da03ad9..4149732fb 100644 --- a/examples/agents/cli_agent.py +++ b/examples/agents/cli_agent.py @@ -1,11 +1,22 @@ -"""Simple test agent for CLI testing. - -run: +""" +Ragbits Agents Example: CLI agent -uv run ragbits agents run examples.agents.test_cli_agent:test_agent +This example demonstrates how to expose an agent via the ragbits CLI, allowing +it to be run interactively from the command line with conversation history. +To expose the CLI Agent simply run: + ```bash + uv run ragbits agents run examples.agents.cli_agent:test_agent + ``` """ +# /// script +# requires-python = ">=3.10" +# dependencies = [ +# "ragbits-agents", +# ] +# /// + from ragbits.agents import Agent from ragbits.core.llms import LiteLLM diff --git a/examples/agents/dependencies.py b/examples/agents/dependencies.py index 4ec80d1b0..5d93b9939 100644 --- a/examples/agents/dependencies.py +++ b/examples/agents/dependencies.py @@ -1,3 +1,23 @@ +""" +Ragbits Agents Example: Dependencies + +This example demonstrates how to use AgentContext and AgentDependencies + +To run the script, execute the following command: + + ```bash + uv run examples/agents/dependencies.py + ``` +""" + +# /// script +# requires-python = ">=3.10" +# dependencies = [ +# "ragbits-core", +# "ragbits-agents", +# ] +# /// + from dataclasses import dataclass from ragbits.agents import Agent, AgentRunContext diff --git a/examples/agents/hooks/agent_output_logging.py b/examples/agents/hooks/agent_output_logging.py index 82555bbcf..642097826 100644 --- a/examples/agents/hooks/agent_output_logging.py +++ b/examples/agents/hooks/agent_output_logging.py @@ -1,10 +1,24 @@ """ -Example demonstrating a post-tool hook for logging outputs from agent tools. +Ragbits Agents Example: Post-tool hook for agent output logging -This shows how to log the output returned by downstream agent tools. -Setup: 1 parent agent with 2 expert child agents as tools. +This example demonstrates how to use a POST_TOOL hook to log outputs returned +by downstream agent tools. The setup consists of a parent agent with two expert +child agents (diet and fitness) registered as tools. + +To execute this script simply run: + ```bash + uv run examples/agents/hooks/agent_output_logging.py + ``` """ +# /// script +# requires-python = ">=3.10" +# dependencies = [ +# "ragbits-core", +# "ragbits-agents", +# ] +# /// + import asyncio from ragbits.agents import Agent diff --git a/examples/agents/hooks/guardrails_integration.py b/examples/agents/hooks/guardrails_integration.py index 0a2b67332..96743dc7d 100644 --- a/examples/agents/hooks/guardrails_integration.py +++ b/examples/agents/hooks/guardrails_integration.py @@ -1,14 +1,24 @@ """ -Example demonstrating guardrails integration using pre-run and on-event hooks. +Ragbits Agents Example: Guardrails integration with hooks -This example shows how to use the ragbits Guardrail system with agent hooks -to validate inputs before the agent processes them and how to use ON_EVENT -hooks to transform streaming output in real-time. +This example demonstrates how to use the ragbits Guardrail system with agent hooks +to validate inputs before the agent processes them, and how to use ON_EVENT hooks +to transform streaming output in real-time. -To run this example, execute: - uv run python examples/agents/hooks/guardrails_integration.py +To execute this script simply run: + ```bash + uv run examples/agents/hooks/guardrails_integration.py + ``` """ +# /// script +# requires-python = ">=3.10" +# dependencies = [ +# "ragbits-agents", +# "ragbits-guardrails", +# ] +# /// + import asyncio from ragbits.agents import Agent diff --git a/examples/agents/hooks/validation_and_sanitization.py b/examples/agents/hooks/validation_and_sanitization.py index c8ef63ff7..4d82255b3 100644 --- a/examples/agents/hooks/validation_and_sanitization.py +++ b/examples/agents/hooks/validation_and_sanitization.py @@ -1,12 +1,23 @@ """ -Example demonstrating hooks for input validation, sanitization, and output masking. +Ragbits Agents Example: Input validation, sanitization, and output masking with hooks -This example shows a customer support agent with hooks that: -- Validate email addresses before sending -- Sanitize email domains to approved list -- Mask sensitive user data in responses +This example demonstrates a customer support agent with PRE_TOOL and POST_TOOL hooks that +validate email addresses before sending, sanitize email domains to an approved list, +and mask sensitive user data in tool responses. + +To execute this script simply run: + ```bash + uv run examples/agents/hooks/validation_and_sanitization.py + ``` """ +# /// script +# requires-python = ">=3.10" +# dependencies = [ +# "ragbits-agents", +# ] +# /// + import asyncio import re from typing import Any diff --git a/examples/chat/file_explorer_agent.py b/examples/chat/file_explorer_agent.py index 3c4f3cfbb..450eadbfd 100644 --- a/examples/chat/file_explorer_agent.py +++ b/examples/chat/file_explorer_agent.py @@ -24,6 +24,14 @@ ragbits api run examples.chat.file_explorer_agent:FileExplorerChat """ +# /// script +# requires-python = ">=3.10" +# dependencies = [ +# "ragbits-chat", +# "ragbits-agents", +# ] +# /// + import json import os from collections.abc import AsyncGenerator diff --git a/examples/chat/themed_chat.py b/examples/chat/themed_chat.py index e0d02b9ce..166b48bb0 100644 --- a/examples/chat/themed_chat.py +++ b/examples/chat/themed_chat.py @@ -11,6 +11,13 @@ That's it! No manual conversion needed! """ +# /// script +# requires-python = ">=3.10" +# dependencies = [ +# "ragbits-chat", +# ] +# /// + from collections.abc import AsyncGenerator from pydantic import BaseModel diff --git a/examples/chat/upload_chat.py b/examples/chat/upload_chat.py index ce1c1b153..8cc2c54ee 100644 --- a/examples/chat/upload_chat.py +++ b/examples/chat/upload_chat.py @@ -11,6 +11,13 @@ ``` """ +# /// script +# requires-python = ">=3.10" +# dependencies = [ +# "ragbits-chat", +# ] +# /// + from collections.abc import AsyncGenerator from fastapi import UploadFile diff --git a/packages/ragbits-agents/src/ragbits/agents/_main.py b/packages/ragbits-agents/src/ragbits/agents/_main.py index 8e1bea2dd..5ed84a6c1 100644 --- a/packages/ragbits-agents/src/ragbits/agents/_main.py +++ b/packages/ragbits-agents/src/ragbits/agents/_main.py @@ -11,10 +11,7 @@ from types import ModuleType, SimpleNamespace from typing import Any, ClassVar, Generic, TypeVar, Union, cast, overload -from pydantic import ( - BaseModel, - Field, -) +from pydantic import BaseModel, Field, PrivateAttr from typing_extensions import Self from ragbits import agents @@ -145,26 +142,20 @@ class AgentDependencies(BaseModel, Generic[DepsT]): model_config = {"arbitrary_types_allowed": True} - _frozen: bool - _value: DepsT | None + _frozen: bool = PrivateAttr(default=False) + _value: DepsT | None = PrivateAttr(default=None) def __init__(self, value: DepsT | None = None) -> None: super().__init__() - self._value = value - self._frozen = False + object.__setattr__(self, "_value", value) + object.__setattr__(self, "_frozen", False) def __setattr__(self, name: str, value: object) -> None: - is_frozen = False - if name != "_frozen": - try: - is_frozen = object.__getattribute__(self, "_frozen") - except AttributeError: - is_frozen = False - + is_frozen = object.__getattribute__(self, "_frozen") if is_frozen and name not in {"_frozen"}: raise RuntimeError("Dependencies are immutable after first access") - super().__setattr__(name, value) + object.__setattr__(self, name, value) @property def value(self) -> DepsT | None: @@ -183,7 +174,7 @@ def _freeze(self) -> None: def __getattr__(self, name: str) -> object: value = object.__getattribute__(self, "_value") if value is None: - raise AttributeError(name) + raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'") self._freeze() return getattr(value, name)