diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 00000000..a2306680 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,58 @@ +# Gradient Python SDK Examples + +This directory contains examples demonstrating how to use the Gradient Python SDK with various frameworks and for different use cases. + +## Available Examples + +### Framework Integrations + +These examples show how to integrate the Gradient Python SDK with popular web frameworks: + +- **[Django Integration](django_integration.py)** - Simple Django views for chat completions, image generation, and agent listing +- **[Flask Integration](flask_integration.py)** - Flask routes demonstrating SDK usage with proper error handling +- **[FastAPI Integration](fastapi_integration.py)** - FastAPI endpoints with Pydantic models and async support + +## Running Examples + +Each example is a standalone Python script that can be run directly: + +```bash +# Make sure you have the required environment variables set +export DIGITALOCEAN_ACCESS_TOKEN="your_token_here" +export GRADIENT_MODEL_ACCESS_KEY="your_model_key_here" +export GRADIENT_AGENT_ACCESS_KEY="your_agent_key_here" +export GRADIENT_AGENT_ENDPOINT="https://your-agent.agents.do-ai.run" + +# Run an example +python examples/django_integration.py +``` + +## Framework-Specific Setup + +### Django +The Django example shows how to create a Django view that uses the Gradient SDK for AI-powered responses. + +### Flask +The Flask example demonstrates integrating Gradient SDK with Flask routes for web applications. + +### FastAPI +The FastAPI example shows how to create async endpoints that leverage the Gradient SDK's async capabilities. + +## Environment Variables + +All examples require proper authentication setup: + +- `DIGITALOCEAN_ACCESS_TOKEN` - For DigitalOcean API operations +- `GRADIENT_MODEL_ACCESS_KEY` - For serverless inference +- `GRADIENT_AGENT_ACCESS_KEY` - For agent-specific operations +- `GRADIENT_AGENT_ENDPOINT` - Your deployed agent endpoint + +## Contributing + +When adding new examples: + +1. Follow the existing naming convention +2. Include comprehensive comments +3. Handle errors appropriately +4. Use environment variables for configuration +5. Add the example to this README \ No newline at end of file diff --git a/examples/django_integration.py b/examples/django_integration.py new file mode 100644 index 00000000..e66bb662 --- /dev/null +++ b/examples/django_integration.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python3 +""" +Django Integration Example for Gradient Python SDK + +This example demonstrates how to integrate the Gradient Python SDK +with a Django application to create AI-powered endpoints. + +Requirements: +- Django +- gradient (this SDK) + +Setup: +1. Install dependencies: pip install django gradient +2. Set environment variables (see below) +3. Run: python manage.py runserver + +Environment Variables Required: +- DIGITALOCEAN_ACCESS_TOKEN +- GRADIENT_MODEL_ACCESS_KEY +""" + +import os +import json +from typing import Dict, Any + +# Django imports +from django.http import JsonResponse +from django.views.decorators.csrf import csrf_exempt +from django.views.decorators.http import require_http_methods + +# Gradient SDK imports +from gradient import Gradient + +# Initialize Gradient client +gradient_client = Gradient( + access_token=os.getenv("DIGITALOCEAN_ACCESS_TOKEN"), + model_access_key=os.getenv("GRADIENT_MODEL_ACCESS_KEY"), +) + + +@csrf_exempt +@require_http_methods(["POST"]) +def chat_completion(request) -> JsonResponse: + """ + Django view for chat completions using Gradient SDK. + + Expects JSON payload: + { + "messages": [{"role": "user", "content": "Hello!"}], + "model": "llama3.3-70b-instruct" + } + """ + try: + data: Dict[str, Any] = json.loads(request.body) + messages = data.get("messages", []) + model = data.get("model", "llama3.3-70b-instruct") + + if not messages: + return JsonResponse({"error": "Messages are required"}, status=400) + + response = gradient_client.chat.completions.create( + messages=messages, + model=model, + ) + + return JsonResponse({ + "response": response.choices[0].message.content, + "model": response.model, + }) + + except json.JSONDecodeError: + return JsonResponse({"error": "Invalid JSON payload"}, status=400) + except Exception as e: + return JsonResponse({"error": str(e)}, status=500) + + +@csrf_exempt +@require_http_methods(["POST"]) +def image_generation(request) -> JsonResponse: + """ + Django view for image generation using Gradient SDK. + + Expects JSON payload: + { + "prompt": "A beautiful sunset over mountains" + } + """ + try: + data: Dict[str, Any] = json.loads(request.body) + prompt = data.get("prompt") + + if not prompt: + return JsonResponse({"error": "Prompt is required"}, status=400) + + response = gradient_client.images.generate(prompt=prompt) + + return JsonResponse({ + "image_url": response.data[0].url, + }) + + except json.JSONDecodeError: + return JsonResponse({"error": "Invalid JSON payload"}, status=400) + except Exception as e: + return JsonResponse({"error": str(e)}, status=500) + + +@require_http_methods(["GET"]) +def list_agents(request) -> JsonResponse: + """ + Django view to list available agents. + + Query parameters: + - limit: Maximum number of agents to return (default: 10) + """ + try: + limit = int(request.GET.get("limit", 10)) + + response = gradient_client.agents.list(limit=limit) + + return JsonResponse({ + "agents": [ + { + "uuid": agent.uuid, + "name": agent.name, + } + for agent in response.agents + ] + }) + + except Exception as e: + return JsonResponse({"error": str(e)}, status=500) + + +# URL patterns for Django +""" +# In your Django urls.py: + +from django.urls import path +from . import views + +urlpatterns = [ + path('api/chat/', views.chat_completion, name='chat_completion'), + path('api/images/generate/', views.image_generation, name='image_generation'), + path('api/agents/', views.list_agents, name='list_agents'), +] + +# Example usage: +# POST /api/chat/ with {"messages": [{"role": "user", "content": "Hello!"}]} +# POST /api/images/generate/ with {"prompt": "A sunset"} +# GET /api/agents/ +""" \ No newline at end of file diff --git a/examples/fastapi_integration.py b/examples/fastapi_integration.py new file mode 100644 index 00000000..dc0427cd --- /dev/null +++ b/examples/fastapi_integration.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 +""" +FastAPI Integration Example for Gradient Python SDK + +This example demonstrates how to integrate the Gradient Python SDK +with a FastAPI application to create AI-powered endpoints. + +Requirements: +- fastapi +- uvicorn +- gradient (this SDK) + +Setup: +1. Install dependencies: pip install fastapi uvicorn gradient +2. Set environment variables (see below) +3. Run: python fastapi_integration.py + +Environment Variables Required: +- DIGITALOCEAN_ACCESS_TOKEN +- GRADIENT_MODEL_ACCESS_KEY +""" + +import os +from typing import List, Dict, Any +from fastapi import FastAPI, HTTPException +from pydantic import BaseModel + +# Gradient SDK imports +from gradient import Gradient + +app = FastAPI(title="Gradient AI API", version="1.0.0") + +# Initialize Gradient client +gradient_client = Gradient( + access_token=os.getenv("DIGITALOCEAN_ACCESS_TOKEN"), + model_access_key=os.getenv("GRADIENT_MODEL_ACCESS_KEY"), +) + + +class ChatMessage(BaseModel): + role: str + content: str + + +class ChatRequest(BaseModel): + messages: List[ChatMessage] + model: str = "llama3.3-70b-instruct" + + +class ImageRequest(BaseModel): + prompt: str + + +@app.post("/api/chat") +async def chat_completion(request: ChatRequest): + """ + FastAPI endpoint for chat completions using Gradient SDK. + """ + try: + messages = [{"role": msg.role, "content": msg.content} for msg in request.messages] + + response = gradient_client.chat.completions.create( + messages=messages, + model=request.model, + ) + + return { + "response": response.choices[0].message.content, + "model": response.model, + } + + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + + +@app.post("/api/images/generate") +async def image_generation(request: ImageRequest): + """ + FastAPI endpoint for image generation using Gradient SDK. + """ + try: + response = gradient_client.images.generate(prompt=request.prompt) + + return { + "image_url": response.data[0].url, + } + + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + + +@app.get("/api/agents") +async def list_agents(limit: int = 10): + """ + FastAPI endpoint to list available agents. + """ + try: + response = gradient_client.agents.list(limit=limit) + + return { + "agents": [ + { + "uuid": agent.uuid, + "name": agent.name, + } + for agent in response.agents + ] + } + + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + + +if __name__ == "__main__": + import uvicorn + uvicorn.run(app, host="0.0.0.0", port=8000) \ No newline at end of file diff --git a/examples/flask_integration.py b/examples/flask_integration.py new file mode 100644 index 00000000..f50f72cd --- /dev/null +++ b/examples/flask_integration.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python3 +""" +Flask Integration Example for Gradient Python SDK + +This example demonstrates how to integrate the Gradient Python SDK +with a Flask application to create AI-powered endpoints. + +Requirements: +- Flask +- gradient (this SDK) + +Setup: +1. Install dependencies: pip install flask gradient +2. Set environment variables (see below) +3. Run: python flask_integration.py + +Environment Variables Required: +- DIGITALOCEAN_ACCESS_TOKEN +- GRADIENT_MODEL_ACCESS_KEY +""" + +import os +from flask import Flask, request, jsonify + +# Gradient SDK imports +from gradient import Gradient + +app = Flask(__name__) + +# Initialize Gradient client +gradient_client = Gradient( + access_token=os.getenv("DIGITALOCEAN_ACCESS_TOKEN"), + model_access_key=os.getenv("GRADIENT_MODEL_ACCESS_KEY"), +) + + +@app.route('/api/chat', methods=['POST']) +def chat_completion(): + """ + Flask endpoint for chat completions using Gradient SDK. + + Expects JSON payload: + { + "messages": [{"role": "user", "content": "Hello!"}], + "model": "llama3.3-70b-instruct" + } + """ + try: + data = request.get_json() + if not data: + return jsonify({"error": "JSON payload required"}), 400 + + messages = data.get("messages", []) + model = data.get("model", "llama3.3-70b-instruct") + + if not messages: + return jsonify({"error": "Messages are required"}), 400 + + response = gradient_client.chat.completions.create( + messages=messages, + model=model, + ) + + return jsonify({ + "response": response.choices[0].message.content, + "model": response.model, + }) + + except Exception as e: + return jsonify({"error": str(e)}), 500 + + +@app.route('/api/images/generate', methods=['POST']) +def image_generation(): + """ + Flask endpoint for image generation using Gradient SDK. + + Expects JSON payload: + { + "prompt": "A beautiful sunset over mountains" + } + """ + try: + data = request.get_json() + if not data: + return jsonify({"error": "JSON payload required"}), 400 + + prompt = data.get("prompt") + if not prompt: + return jsonify({"error": "Prompt is required"}), 400 + + response = gradient_client.images.generate(prompt=prompt) + + return jsonify({ + "image_url": response.data[0].url, + }) + + except Exception as e: + return jsonify({"error": str(e)}), 500 + + +@app.route('/api/agents', methods=['GET']) +def list_agents(): + """ + Flask endpoint to list available agents. + + Query parameters: + - limit: Maximum number of agents to return (default: 10) + """ + try: + limit = int(request.args.get("limit", 10)) + + response = gradient_client.agents.list(limit=limit) + + return jsonify({ + "agents": [ + { + "uuid": agent.uuid, + "name": agent.name, + } + for agent in response.agents + ] + }) + + except Exception as e: + return jsonify({"error": str(e)}), 500 + + +if __name__ == '__main__': + app.run(debug=True, host='0.0.0.0', port=5000) \ No newline at end of file diff --git a/tests/test_examples.py b/tests/test_examples.py new file mode 100644 index 00000000..720a63fc --- /dev/null +++ b/tests/test_examples.py @@ -0,0 +1,289 @@ +"""Tests for example integrations to ensure they work correctly.""" + +from __future__ import annotations + +import os +import pytest +from unittest.mock import Mock, patch, MagicMock + +# Skip all tests if environment variables are not set +pytestmark = pytest.mark.skipif( + not os.getenv("DIGITALOCEAN_ACCESS_TOKEN") or not os.getenv("GRADIENT_MODEL_ACCESS_KEY"), + reason="Requires DIGITALOCEAN_ACCESS_TOKEN and GRADIENT_MODEL_ACCESS_KEY environment variables" +) + + +class TestDjangoIntegration: + """Test Django integration example functions.""" + + def test_django_example_imports(self): + """Test that Django integration example can be imported without errors.""" + try: + import sys + import importlib.util + + # Load the Django example module + spec = importlib.util.spec_from_file_location( + "django_integration", + "examples/django_integration.py" + ) + django_module = importlib.util.module_from_spec(spec) + sys.modules["django_integration"] = django_module + + spec.loader.exec_module(django_module) + + # Check that key functions exist + assert hasattr(django_module, 'chat_completion') + assert hasattr(django_module, 'image_generation') + assert hasattr(django_module, 'list_agents') + + except Exception as e: + pytest.fail(f"Django integration example has errors: {e}") + + def test_flask_example_imports(self): + """Test that Flask integration example can be imported without errors.""" + try: + import sys + import importlib.util + + # Load the Flask example module + spec = importlib.util.spec_from_file_location( + "flask_integration", + "examples/flask_integration.py" + ) + flask_module = importlib.util.module_from_spec(spec) + sys.modules["flask_integration"] = flask_module + + spec.loader.exec_module(flask_module) + + # Check that Flask app exists + assert hasattr(flask_module, 'app') + + except Exception as e: + pytest.fail(f"Flask integration example has errors: {e}") + + def test_fastapi_example_imports(self): + """Test that FastAPI integration example can be imported without errors.""" + try: + import sys + import importlib.util + + # Load the FastAPI example module + spec = importlib.util.spec_from_file_location( + "fastapi_integration", + "examples/fastapi_integration.py" + ) + fastapi_module = importlib.util.module_from_spec(spec) + sys.modules["fastapi_integration"] = fastapi_module + + spec.loader.exec_module(fastapi_module) + + # Check that FastAPI app exists + assert hasattr(fastapi_module, 'app') + + except Exception as e: + pytest.fail(f"FastAPI integration example has errors: {e}") + + +class TestStreamlitIntegration: + """Test Streamlit integration example.""" + + def test_streamlit_example_imports(self): + """Test that Streamlit integration example can be imported without errors.""" + try: + import sys + import importlib.util + + # Load the Streamlit example module + spec = importlib.util.spec_from_file_location( + "streamlit_integration", + "examples/streamlit_integration.py" + ) + streamlit_module = importlib.util.module_from_spec(spec) + sys.modules["streamlit_integration"] = streamlit_module + + spec.loader.exec_module(streamlit_module) + + # Check that key functions exist + assert hasattr(streamlit_module, 'chat_completion') + assert hasattr(streamlit_module, 'generate_image') + assert hasattr(streamlit_module, 'list_agents') + + except Exception as e: + pytest.fail(f"Streamlit integration example has errors: {e}") + + +class TestGradioIntegration: + """Test Gradio integration example.""" + + def test_gradio_example_imports(self): + """Test that Gradio integration example can be imported without errors.""" + try: + import sys + import importlib.util + + # Load the Gradio example module + spec = importlib.util.spec_from_file_location( + "gradio_integration", + "examples/gradio_integration.py" + ) + gradio_module = importlib.util.module_from_spec(spec) + sys.modules["gradio_integration"] = gradio_module + + spec.loader.exec_module(gradio_module) + + # Check that key functions exist + assert hasattr(gradio_module, 'chat_completion') + assert hasattr(gradio_module, 'generate_image') + assert hasattr(gradio_module, 'list_agents') + assert hasattr(gradio_module, 'create_demo') + + except Exception as e: + pytest.fail(f"Gradio integration example has errors: {e}") + + +class TestStreamlitIntegration: + """Test Streamlit integration example.""" + + def test_streamlit_example_imports(self): + """Test that Streamlit example can be imported without errors.""" + try: + import sys + import importlib.util + + # Load the Streamlit example module + spec = importlib.util.spec_from_file_location( + "streamlit_integration", + "examples/streamlit_integration.py" + ) + streamlit_module = importlib.util.module_from_spec(spec) + sys.modules["streamlit_integration"] = streamlit_module + + spec.loader.exec_module(streamlit_module) + + # Check that key functions exist + assert hasattr(streamlit_module, 'chat_completion') + assert hasattr(streamlit_module, 'generate_image') + assert hasattr(streamlit_module, 'list_agents') + + except Exception as e: + pytest.fail(f"Streamlit integration example has errors: {e}") + + +class TestGradioIntegration: + """Test Gradio integration example.""" + + def test_gradio_example_imports(self): + """Test that Gradio example can be imported without errors.""" + try: + import sys + import importlib.util + + # Load the Gradio example module + spec = importlib.util.spec_from_file_location( + "gradio_integration", + "examples/gradio_integration.py" + ) + gradio_module = importlib.util.module_from_spec(spec) + sys.modules["gradio_integration"] = gradio_module + + spec.loader.exec_module(gradio_module) + + # Check that key functions exist + assert hasattr(gradio_module, 'chat_completion') + assert hasattr(gradio_module, 'generate_image') + assert hasattr(gradio_module, 'list_agents') + assert hasattr(gradio_module, 'create_demo') + + except Exception as e: + pytest.fail(f"Gradio integration example has errors: {e}") + + +class TestExampleREADME: + """Test that the examples README is properly structured.""" + + def test_readme_exists_and_has_content(self): + """Test that README.md exists and has substantial content.""" + readme_path = "examples/README.md" + + assert os.path.exists(readme_path), "examples/README.md should exist" + + with open(readme_path, 'r', encoding='utf-8') as f: + content = f.read() + + # Check that it has substantial content + assert len(content) > 500, "README should have substantial content" + + # Check for key sections + assert "## Available Examples" in content + assert "## Framework Integrations" in content + assert "## Running Examples" in content + assert "## Environment Variables" in content + + # Check that all examples are mentioned + assert "Django Integration" in content + assert "Flask Integration" in content + assert "FastAPI Integration" in content + assert "Streamlit Integration" in content + assert "Gradio Integration" in content + assert "CLI Example" in content + + +class TestExampleFileStructure: + """Test that example files have proper structure.""" + + def test_all_example_files_exist(self): + """Test that all example files exist.""" + example_files = [ + "examples/README.md", + "examples/django_integration.py", + "examples/flask_integration.py", + "examples/fastapi_integration.py", + ] + + for file_path in example_files: + assert os.path.exists(file_path), f"Example file {file_path} should exist" + + def test_example_files_have_shebang(self): + """Test that Python example files have proper shebang.""" + python_files = [ + "examples/django_integration.py", + "examples/flask_integration.py", + "examples/fastapi_integration.py", + ] + + for file_path in python_files: + with open(file_path, 'r', encoding='utf-8') as f: + first_line = f.readline().strip() + assert first_line.startswith("#!/usr/bin/env python"), \ + f"{file_path} should have proper shebang" + + def test_example_files_have_docstrings(self): + """Test that example files have module docstrings.""" + python_files = [ + "examples/django_integration.py", + "examples/flask_integration.py", + "examples/fastapi_integration.py", + ] + + for file_path in python_files: + with open(file_path, 'r', encoding='utf-8') as f: + content = f.read() + + # Check for module docstring + lines = content.split('\n') + assert len(lines) > 3, f"{file_path} should have content" + + # Look for docstring pattern + docstring_found = False + in_docstring = False + for line in lines[:20]: # Check first 20 lines + stripped = line.strip() + if stripped.startswith('"""') and not in_docstring: + in_docstring = True + docstring_found = True + elif stripped.endswith('"""') and in_docstring: + in_docstring = False + break + + assert docstring_found, f"{file_path} should have a module docstring" \ No newline at end of file