diff --git a/.env.example b/.env.example new file mode 100644 index 000000000..60ffe5bc8 --- /dev/null +++ b/.env.example @@ -0,0 +1,19 @@ +# AgentOps Development Environment Variables +# Copy this file to .env and fill in your actual API keys + +# AgentOps Configuration +AGENTOPS_API_KEY=your_agentops_api_key_here + +# LLM Provider API Keys (for testing) +OPENAI_API_KEY=your_openai_api_key_here +ANTHROPIC_API_KEY=your_anthropic_api_key_here +COHERE_API_KEY=your_cohere_api_key_here +MISTRAL_API_KEY=your_mistral_api_key_here + +# Optional: Additional Provider Keys +# GOOGLE_API_KEY=your_google_api_key_here +# HUGGINGFACE_API_KEY=your_huggingface_api_key_here + +# Development Settings +# AGENTOPS_ENV=development +# AGENTOPS_DEBUG=true diff --git a/.github/workflows/nextjs.yml b/.github/workflows/nextjs.yml new file mode 100644 index 000000000..ed7473670 --- /dev/null +++ b/.github/workflows/nextjs.yml @@ -0,0 +1,93 @@ +# Sample workflow for building and deploying a Next.js site to GitHub Pages +# +# To get started with Next.js see: https://nextjs.org/docs/getting-started +# +name: Deploy Next.js site to Pages + +on: + # Runs on pushes targeting the default branch + push: + branches: ["main"] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + # Build job + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Detect package manager + id: detect-package-manager + run: | + if [ -f "${{ github.workspace }}/yarn.lock" ]; then + echo "manager=yarn" >> $GITHUB_OUTPUT + echo "command=install" >> $GITHUB_OUTPUT + echo "runner=yarn" >> $GITHUB_OUTPUT + exit 0 + elif [ -f "${{ github.workspace }}/package.json" ]; then + echo "manager=npm" >> $GITHUB_OUTPUT + echo "command=ci" >> $GITHUB_OUTPUT + echo "runner=npx --no-install" >> $GITHUB_OUTPUT + exit 0 + else + echo "Unable to determine package manager" + exit 1 + fi + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: ${{ steps.detect-package-manager.outputs.manager }} + - name: Setup Pages + uses: actions/configure-pages@v5 + with: + # Automatically inject basePath in your Next.js configuration file and disable + # server side image optimization (https://nextjs.org/docs/api-reference/next/image#unoptimized). + # + # You may remove this line if you want to manage the configuration yourself. + static_site_generator: next + - name: Restore cache + uses: actions/cache@v4 + with: + path: | + .next/cache + # Generate a new cache whenever packages or source files change. + key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }} + # If source files changed but packages didn't, rebuild from a prior cache. + restore-keys: | + ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}- + - name: Install dependencies + run: ${{ steps.detect-package-manager.outputs.manager }} ${{ steps.detect-package-manager.outputs.command }} + - name: Build with Next.js + run: ${{ steps.detect-package-manager.outputs.runner }} next build + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: ./out + + # Deployment job + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/GENSPARK_SETUP.md b/GENSPARK_SETUP.md new file mode 100644 index 000000000..508c82b37 --- /dev/null +++ b/GENSPARK_SETUP.md @@ -0,0 +1,144 @@ +# GenSpark AI Developer Setup + +This document describes the setup and workflow for the GenSpark AI Developer branch. + +## Branch Information + +- **Branch Name**: `genspark_ai_developer` +- **Purpose**: AI-assisted development and enhancements for AgentOps +- **Base Branch**: `main` + +## Setup Completed + +### ✅ Environment Configuration + +1. **Branch Created**: `genspark_ai_developer` branch has been created and checked out +2. **Python Version**: Python 3.12.11 +3. **Package Manager**: pip 25.0.1 +4. **Pre-commit Hooks**: Installed and configured with ruff linter + +### ✅ Dependencies Installed + +The following dependency groups have been installed: + +- **Core Dependencies**: All required packages from `pyproject.toml` +- **Development Tools**: pytest, ruff, mypy, and other dev tools +- **OpenTelemetry Stack**: Full observability stack for monitoring +- **Pre-commit**: Code quality enforcement tools + +## Development Workflow + +### Making Changes + +1. **Make your code changes** in the appropriate files +2. **Test your changes** locally: + ```bash + pytest tests/ + ``` + +3. **MANDATORY: Commit immediately after changes**: + ```bash + git add . + git commit -m "type(scope): description" + ``` + +4. **Sync with remote before PR**: + ```bash + git fetch origin main + git rebase origin/main + # Resolve any conflicts, prioritizing remote code + ``` + +5. **Squash all commits into one**: + ```bash + # Count your commits first + git log --oneline + # Squash N commits (replace N with your number) + git reset --soft HEAD~N + git commit -m "comprehensive commit message describing all changes" + ``` + +6. **Push to remote**: + ```bash + git push -f origin genspark_ai_developer + ``` + +7. **Create or Update Pull Request**: + - Create PR from `genspark_ai_developer` to `main` + - Include comprehensive description + - Share PR link with team + +### Commit Message Format + +Use conventional commit format: +``` +type(scope): description + +Examples: +- feat(llms): add support for new LLM provider +- fix(client): resolve connection timeout issue +- docs(readme): update installation instructions +- test(integration): add tests for new feature +``` + +### Code Quality + +Pre-commit hooks will automatically: +- Run ruff linter with auto-fix +- Format code with ruff-format +- Ensure code quality before each commit + +## Repository Structure + +``` +agentops/ +├── agentops/ # Main SDK package +│ ├── llms/ # LLM provider integrations +│ ├── sdk/ # Core SDK functionality +│ └── ... +├── app/ # Dashboard application +├── tests/ # Test suite +├── examples/ # Usage examples +├── docs/ # Documentation +└── pyproject.toml # Project configuration +``` + +## Testing + +Run tests with: +```bash +# All tests +pytest tests/ + +# Specific test file +pytest tests/llms/test_anthropic.py -v + +# With coverage +coverage run -m pytest +coverage report +``` + +## Contributing + +See [CONTRIBUTING.md](CONTRIBUTING.md) for detailed contribution guidelines. + +## Important Notes + +- **NO UNCOMMITTED CHANGES**: Every code change must be committed +- **SYNC BEFORE PR**: Always fetch and merge latest remote changes before creating PR +- **SQUASH COMMITS**: Combine all commits into one comprehensive commit +- **RESOLVE CONFLICTS**: Prioritize remote code when resolving conflicts +- **SHARE PR LINK**: Always provide the PR URL after creation/update + +## Resources + +- [Main README](README.md) +- [Contributing Guide](CONTRIBUTING.md) +- [AgentOps Documentation](https://docs.agentops.ai) +- [Discord Community](https://discord.gg/FagdcwwXRR) + +## Setup Date + +- **Created**: 2025-10-19 +- **Python Version**: 3.12.11 +- **Branch Status**: Clean, ready for development diff --git a/dev-scripts.sh b/dev-scripts.sh new file mode 100755 index 000000000..6bf55241b --- /dev/null +++ b/dev-scripts.sh @@ -0,0 +1,200 @@ +#!/bin/bash +# Development Helper Scripts for AgentOps + +set -e # Exit on error + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Function to print colored messages +print_status() { + echo -e "${GREEN}[✓]${NC} $1" +} + +print_error() { + echo -e "${RED}[✗]${NC} $1" +} + +print_warning() { + echo -e "${YELLOW}[!]${NC} $1" +} + +# Check current branch +check_branch() { + CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) + if [ "$CURRENT_BRANCH" != "genspark_ai_developer" ]; then + print_warning "You are on branch '$CURRENT_BRANCH', not 'genspark_ai_developer'" + read -p "Continue anyway? (y/n) " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + exit 1 + fi + fi +} + +# Setup development environment +setup() { + print_status "Setting up development environment..." + + # Install dependencies + print_status "Installing dependencies..." + pip install -e ".[dev]" + + # Install pre-commit hooks + print_status "Installing pre-commit hooks..." + python3 -m pre_commit install + + # Create .env file if it doesn't exist + if [ ! -f .env ]; then + print_warning ".env file not found. Creating from .env.example..." + cp .env.example .env + print_warning "Please edit .env file with your actual API keys" + fi + + print_status "Setup complete!" +} + +# Run tests +test() { + print_status "Running tests..." + pytest tests/ -v +} + +# Run tests with coverage +test_coverage() { + print_status "Running tests with coverage..." + coverage run -m pytest + coverage report + coverage html + print_status "Coverage report generated in htmlcov/index.html" +} + +# Run linter +lint() { + print_status "Running linter..." + python3 -m pre_commit run --all-files +} + +# Sync with upstream +sync() { + check_branch + print_status "Syncing with upstream main..." + + # Fetch latest changes + git fetch origin main + + # Check for uncommitted changes + if ! git diff-index --quiet HEAD --; then + print_error "You have uncommitted changes. Please commit or stash them first." + exit 1 + fi + + # Rebase on main + print_status "Rebasing on origin/main..." + git rebase origin/main + + print_status "Sync complete!" +} + +# Squash commits +squash() { + check_branch + + # Count commits ahead of main + COMMITS_AHEAD=$(git rev-list --count origin/main..HEAD) + + if [ "$COMMITS_AHEAD" -eq 0 ]; then + print_warning "No commits to squash" + exit 0 + fi + + print_status "Found $COMMITS_AHEAD commits ahead of main" + read -p "Squash all $COMMITS_AHEAD commits? (y/n) " -n 1 -r + echo + + if [[ $REPLY =~ ^[Yy]$ ]]; then + print_status "Squashing commits..." + git reset --soft HEAD~$COMMITS_AHEAD + + print_status "Enter commit message (Ctrl+D when done):" + COMMIT_MSG=$(cat) + + git commit -m "$COMMIT_MSG" + print_status "Commits squashed successfully!" + print_warning "Don't forget to push with: git push -f origin genspark_ai_developer" + fi +} + +# Create PR workflow +prepare_pr() { + check_branch + print_status "Preparing for Pull Request..." + + # Sync with main + print_status "Step 1: Syncing with main..." + sync + + # Squash commits + print_status "Step 2: Squashing commits..." + squash + + # Push to remote + print_status "Step 3: Pushing to remote..." + read -p "Push to origin? (y/n) " -n 1 -r + echo + + if [[ $REPLY =~ ^[Yy]$ ]]; then + git push -f origin genspark_ai_developer + print_status "Pushed successfully!" + print_status "Next step: Create PR from genspark_ai_developer to main" + print_status "GitHub URL: https://github.com/MrBomaye/agentops/compare/main...genspark_ai_developer" + fi +} + +# Show help +help() { + echo "AgentOps Development Scripts" + echo "" + echo "Usage: ./dev-scripts.sh [command]" + echo "" + echo "Commands:" + echo " setup - Setup development environment" + echo " test - Run tests" + echo " test_coverage - Run tests with coverage report" + echo " lint - Run linter and code formatter" + echo " sync - Sync with upstream main branch" + echo " squash - Squash all commits into one" + echo " prepare_pr - Complete workflow: sync, squash, push" + echo " help - Show this help message" +} + +# Main script logic +case "$1" in + setup) + setup + ;; + test) + test + ;; + test_coverage) + test_coverage + ;; + lint) + lint + ;; + sync) + sync + ;; + squash) + squash + ;; + prepare_pr) + prepare_pr + ;; + help|*) + help + ;; +esac diff --git a/llms.txt b/llms.txt index ad2313a40..672434cb6 100644 --- a/llms.txt +++ b/llms.txt @@ -8,6 +8,10 @@ AgentOps helps developers build, evaluate, and monitor AI agents. From prototype to production. +## Open Source + +The AgentOps app is open source under the MIT license. Explore the code in our [app directory](https://github.com/AgentOps-AI/agentops/tree/main/app). + ## Key Integrations ## Quick Start @@ -36,6 +40,12 @@ agentops.end_session('Success') All your sessions can be viewed on the [AgentOps dashboard](https://app.agentops.ai?ref=gh) +## Self-Hosting + +Looking to run the full AgentOps app (Dashboard + API backend) on your machine? Follow the setup guide in `app/README.md`: + +- [Run the App and Backend (Dashboard + API)](https://github.com/AgentOps-AI/agentops/blob/main/app/README.md) + Agent Debugging Session Replays @@ -1576,11 +1586,12 @@ __all__ = [ ```python import atexit +import asyncio +import threading from typing import Optional, Any from agentops.client.api import ApiClient from agentops.config import Config -from agentops.exceptions import NoApiKeyException from agentops.instrumentation import instrument_all from agentops.logging import logger from agentops.logging.config import configure_logging, intercept_opentelemetry_logging @@ -1617,13 +1628,17 @@ class Client: config: Config _initialized: bool _init_trace_context: Optional[TraceContext] = None # Stores the context of the auto-started trace - _legacy_session_for_init_trace: Optional[ - Session - ] = None # Stores the legacy Session wrapper for the auto-started trace + _legacy_session_for_init_trace: Optional[Session] = ( + None # Stores the legacy Session wrapper for the auto-started trace + ) __instance = None # Class variable for singleton pattern api: ApiClient + _auth_token: Optional[str] = None + _project_id: Optional[str] = None + _auth_lock = threading.Lock() + _auth_task: Optional[asyncio.Task] = None def __new__(cls, *args: Any, **kwargs: Any) -> "Client": if cls.__instance is None: @@ -1631,6 +1646,10 @@ class Client: # Initialize instance variables that should only be set once per instance cls.__instance._init_trace_context = None cls.__instance._legacy_session_for_init_trace = None + cls.__instance._auth_token = None + cls.__instance._project_id = None + cls.__instance._auth_lock = threading.Lock() + cls.__instance._auth_task = None return cls.__instance def __init__(self): @@ -1645,6 +1664,73 @@ class Client: # self._init_trace_context = None # Already done in __new__ # self._legacy_session_for_init_trace = None # Already done in __new__ + def get_current_jwt(self) -> Optional[str]: + """Get the current JWT token.""" + with self._auth_lock: + return self._auth_token + + def _set_auth_data(self, token: str, project_id: str): + """Set authentication data thread-safely.""" + with self._auth_lock: + self._auth_token = token + self._project_id = project_id + + # Update the HTTP client's project ID + from agentops.client.http.http_client import HttpClient + + HttpClient.set_project_id(project_id) + + async def _fetch_auth_async(self, api_key: str) -> Optional[dict]: + """Asynchronously fetch authentication token.""" + try: + response = await self.api.v3.fetch_auth_token(api_key) + if response: + self._set_auth_data(response["token"], response["project_id"]) + + # Update V4 client with token + self.api.v4.set_auth_token(response["token"]) + + # Update tracer config with real project ID + tracing_config = {"project_id": response["project_id"]} + tracer.update_config(tracing_config) + + logger.debug("Successfully fetched authentication token asynchronously") + return response + else: + logger.debug("Authentication failed - will continue without authentication") + return None + except Exception: + return None + + def _start_auth_task(self, api_key: str): + """Start the async authentication task.""" + if self._auth_task and not self._auth_task.done(): + return # Task already running + + try: + loop = asyncio.get_event_loop() + if loop.is_running(): + # Use existing event loop + self._auth_task = loop.create_task(self._fetch_auth_async(api_key)) + else: + # Create new event loop in background thread + def run_async_auth(): + asyncio.run(self._fetch_auth_async(api_key)) + + import threading + + auth_thread = threading.Thread(target=run_async_auth, daemon=True) + auth_thread.start() + except RuntimeError: + # Create new event loop in background thread + def run_async_auth(): + asyncio.run(self._fetch_auth_async(api_key)) + + import threading + + auth_thread = threading.Thread(target=run_async_auth, daemon=True) + auth_thread.start() + def init(self, **kwargs: Any) -> None: # Return type updated to None # Recreate the Config object to parse environment variables at the time of initialization # This allows re-init with new env vars if needed, though true singletons usually init once. @@ -1671,35 +1757,36 @@ class Client: return None # If not auto-starting, and already initialized, return None if not self.config.api_key: - raise NoApiKeyException + logger.warning( + "No API key provided. AgentOps will initialize but authentication will fail. " + "Set AGENTOPS_API_KEY environment variable or pass api_key parameter." + ) + # Continue without API key - spans will be created but exports will fail gracefully configure_logging(self.config) intercept_opentelemetry_logging() self.api = ApiClient(self.config.endpoint) - try: - response = self.api.v3.fetch_auth_token(self.config.api_key) - if response is None: - # If auth fails, we cannot proceed with tracer initialization that depends on project_id - logger.error("Failed to fetch auth token. AgentOps SDK will not be initialized.") - return None # Explicitly return None if auth fails - except Exception as e: - # Re-raise authentication exceptions so they can be caught by tests and calling code - logger.error(f"Authentication failed: {e}") - raise - - self.api.v4.set_auth_token(response["token"]) - + # Initialize tracer with JWT provider for dynamic updates tracing_config = self.config.dict() - tracing_config["project_id"] = response["project_id"] + tracing_config["project_id"] = "temporary" # Will be updated when auth completes - tracer.initialize_from_config(tracing_config, jwt=response["token"]) + # Create JWT provider function for dynamic updates + def jwt_provider(): + return self.get_current_jwt() + + # Initialize tracer with JWT provider + tracer.initialize_from_config(tracing_config, jwt_provider=jwt_provider) if self.config.instrument_llm_calls: instrument_all() - # self._initialized = True # Set initialized to True here - MOVED to after trace start attempt + # Start authentication task only if we have an API key + if self.config.api_key: + self._start_auth_task(self.config.api_key) + else: + logger.debug("No API key available - skipping authentication task") global _atexit_registered if not _atexit_registered: @@ -1779,14 +1866,6 @@ class Client: # Consumers should use agentops.start_trace() or rely on the auto-init trace. # For a transition, the auto-init trace's legacy wrapper is set to legacy module's globals. - -# Ensure the global _active_session (if needed for some very old compatibility) points to the client's legacy session for init trace. -# This specific global _active_session in client.py is problematic and should be phased out. -# For now, _client_legacy_session_for_init_trace is the primary global for the auto-init trace's legacy Session. - -# Remove the old global _active_session defined at the top of this file if it's no longer the primary mechanism. -# The new globals _client_init_trace_context and _client_legacy_session_for_init_trace handle the auto-init trace. - ``` ### agentops/sdk/decorators/__init__.py @@ -1858,6 +1937,8 @@ description: "AgentOps is the developer favorite platform for testing, debugging Prefer asking your IDE? Install the Mintlify MCP Docs Server for AgentOps to chat with the docs while you code: `npx mint-mcp add agentops` +The AgentOps app is open source. Browse the code or contribute in our GitHub app directory. + ## Integrate with developer favorite LLM providers and agent frameworks ### Agent Frameworks @@ -1867,6 +1948,7 @@ Prefer asking your IDE? Install the Mintlify MCP Docs Server for AgentOps to cha } iconType="image" href="/v2/integrations/autogen" /> } iconType="image" href="/v2/integrations/crewai" /> } iconType="image" href="/v2/integrations/google_adk" /> + } iconType="image" href="/v2/integrations/haystack" /> } iconType="image" href="/v2/integrations/langchain" /> } iconType="image" href="/v2/integrations/openai_agents_python" /> } iconType="image" href="/v2/integrations/openai_agents_js" /> @@ -1881,6 +1963,7 @@ Prefer asking your IDE? Install the Mintlify MCP Docs Server for AgentOps to cha } iconType="image" href="/v2/integrations/ibm_watsonx_ai" /> } iconType="image" href="/v2/integrations/xai" /> } iconType="image" href="/v2/integrations/mem0" /> + } iconType="image" href="/v2/integrations/memori" /> Observability and monitoring for your AI agents and LLM apps. And we do it all in just two lines of code... @@ -1953,6 +2036,8 @@ AgentOps is designed for easy integration into your AI agent projects, providing [Give us a star on GitHub!](https://github.com/AgentOps-AI/agentops) Your support helps us grow. +The AgentOps app is open sourceexplore the code in our GitHub app directory. + Prefer asking your IDE? Install the Mintlify MCP Docs Server for AgentOps to chat with the docs while you code: `npx mint-mcp add agentops` @@ -1966,7 +2051,7 @@ First, install the AgentOps SDK. We recommend including `python-dotenv` for easy poetry add agentops python-dotenv ``` ```bash uv - uv add agentops python-dotenv + uv pip install agentops python-dotenv ``` ## Initial Setup (2 Lines of Code) @@ -2387,6 +2472,8 @@ description: "Start using AgentOps with just 2 lines of code" import CodeTooltip from '/snippets/add-code-tooltip.mdx' import EnvTooltip from '/snippets/add-env-tooltip.mdx' +The AgentOps app is open sourceexplore the code in our GitHub app directory. + ```bash pip pip install agentops ``` @@ -2618,6 +2705,18 @@ AGENTIC_LIBRARIES: dict[str, InstrumentorConfig] = { "class_name": "LanggraphInstrumentor", "min_version": "0.2.0", }, + "xpander_sdk": { + "module_name": "agentops.instrumentation.agentic.xpander", + "class_name": "XpanderInstrumentor", + "min_version": "1.0.0", + "package_name": "xpander-sdk", + }, + "haystack": { + "module_name": "agentops.instrumentation.agentic.haystack", + "class_name": "HaystackInstrumentor", + "min_version": "2.0.0", + "package_name": "haystack-ai", + }, } # Combine all target packages for monitoring @@ -2838,7 +2937,7 @@ def _perform_instrumentation(package_name: str): _has_agentic_library = True # Special case: If mem0 is instrumented, also instrument concurrent.futures - if package_name == "mem0" and is_newly_added: + if (package_name == "mem0" or package_name == "autogen") and is_newly_added: try: # Check if concurrent.futures module is available