Metadata-Version: 2.4
Name: agent-framework-lib
Version: 0.1.1
Summary: A comprehensive Python framework for building and serving conversational AI agents with FastAPI
Author-email: Sebastian Pavel <sebastian@cinco.ai>
Maintainer-email: Sebastian Pavel <sebastian@cinco.ai>
License-Expression: MIT
Project-URL: Homepage, https://github.com/Cinco-AI/AgentFramework
Project-URL: Repository, https://github.com/Cinco-AI/AgentFramework.git
Project-URL: Issues, https://github.com/Cinco-AI/AgentFramework/issues
Project-URL: Documentation, https://github.com/Cinco-AI/AgentFramework/blob/main/README.md
Project-URL: Changelog, https://github.com/Cinco-AI/AgentFramework/blob/main/CHANGELOG.md
Keywords: ai,agents,fastapi,autogen,framework,conversational-ai,multi-agent,llm,openai,gemini,chatbot,session-management
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Communications :: Chat
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
Classifier: Framework :: FastAPI
Classifier: Environment :: Web Environment
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: aiofiles>=24.1.0
Requires-Dist: autogen-agentchat>=0.6.4
Requires-Dist: autogen-core>=0.6.4
Requires-Dist: autogen-ext[mcp,openai]>=0.6.4
Requires-Dist: fastapi>=0.115.12
Requires-Dist: fastmcp>=2.2.7
Requires-Dist: pyyaml>=6.0.2
Requires-Dist: uvicorn>=0.34.2
Requires-Dist: opentelemetry-sdk>=1.33.1
Requires-Dist: opentelemetry-api>=1.33.1
Requires-Dist: opentelemetry-exporter-otlp-proto-grpc>=1.33.1
Requires-Dist: mcp-python-interpreter
Requires-Dist: pymongo>=4.10.1
Requires-Dist: motor>=3.6.0
Requires-Dist: black>=25.1.0
Provides-Extra: dev
Requires-Dist: pytest>=8.4.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
Requires-Dist: pytest-cov>=6.2.1; extra == "dev"
Requires-Dist: black>=23.0.0; extra == "dev"
Requires-Dist: flake8>=6.0.0; extra == "dev"
Requires-Dist: mypy>=1.5.0; extra == "dev"
Requires-Dist: aiohttp>=3.12.13; extra == "dev"
Requires-Dist: httpx>=0.28.1; extra == "dev"
Provides-Extra: mongodb
Requires-Dist: pymongo>=4.10.1; extra == "mongodb"
Requires-Dist: motor>=3.6.0; extra == "mongodb"
Provides-Extra: all
Requires-Dist: agent-framework-lib[dev,mongodb]; extra == "all"
Dynamic: license-file

# Agent Framework Library

A comprehensive Python framework for building and serving conversational AI agents with FastAPI. Features automatic multi-provider support (OpenAI, Gemini), dynamic configuration, session management, streaming responses, and a rich web interface.

**🎉 NEW: Library Usage** - The Agent Framework can now be installed as an external dependency from GitHub repositories. See [Library Usage Guide](docs/library_usage.md) for details.

## Library Installation

```bash
# Install from GitHub (HTTPS - works with public/private repos with token)
pip install git+https://github.com/Cinco-AI/AgentFramework.git

# Install from GitHub (SSH - requires SSH key setup)
pip install git+ssh://git@github.com/Cinco-AI/AgentFramework.git

# Install from local source (development)
pip install -e .
```

## 🚀 Features

### Core Capabilities
- **Multi-Provider Support**: Automatic routing between OpenAI and Gemini APIs
- **Dynamic System Prompts**: Session-based system prompt control
- **Agent Configuration**: Runtime model parameter adjustment
- **Session Management**: Persistent conversation handling with structured workflow
- **Session Workflow**: Initialize/end session lifecycle with immutable configurations  
- **User Feedback System**: Message-level thumbs up/down and session-level flags
- **Media Detection**: Automatic detection and handling of generated images/videos
- **Web Interface**: Built-in test application with rich UI controls
- **Debug Logging**: Comprehensive logging for system prompts and model configuration

### Advanced Features
- **Model Auto-Detection**: Automatic provider selection based on model name
- **Parameter Filtering**: Provider-specific parameter validation (e.g., Gemini doesn't support frequency_penalty)
- **Configuration Validation**: Built-in validation and status endpoints
- **Correlation & Conversation Tracking**: Link sessions across agents and track individual exchanges
- **Manager Agent Support**: Built-in coordination features for multi-agent workflows
- **Persistent Session Storage**: MongoDB integration for scalable session persistence (see [MongoDB Session Storage Guide](docs/mongodb_session_storage.md))
- **Agent Identity Support**: Multi-agent deployment support with automatic agent identification in MongoDB (see [Agent Identity Guide](docs/agent-identity-support.md))
- **Reverse Proxy Support**: Automatic path prefix detection for deployment behind reverse proxies (see [Reverse Proxy Setup Guide](REVERSE_PROXY_SETUP.md))
- **Backward Compatibility**: Existing implementations continue to work

## 🚀 Quick Start

### Library Usage (Recommended)

The easiest way to use the Agent Framework is with the convenience function:

```python
from agent_framework import AgentInterface, StructuredAgentInput, StructuredAgentOutput, create_basic_agent_server

class MyAgent(AgentInterface):
    async def get_metadata(self):
        return {"name": "My Agent", "version": "1.0.0"}
    
    async def handle_message(self, session_id: str, agent_input: StructuredAgentInput):
        return StructuredAgentOutput(response_text=f"Hello! You said: {agent_input.query}")

# Start server with one line - no server.py file needed!
create_basic_agent_server(MyAgent, port=8000)
```

This automatically handles server setup, routing, and all framework features.

See [examples/](examples/) for complete examples and [docs/library_usage.md](docs/library_usage.md) for comprehensive documentation.

## 📋 Table of Contents

- [Features](#-features)
- [Quick Start](#-quick-start)
- [Configuration](#️-configuration)
- [API Reference](#-api-reference)
- [Client Examples](#-client-examples)
- [Web Interface](#-web-interface)
- [Advanced Usage](#-advanced-usage)
- [Development](#️-development)
- [Authentication](#-authentication)
- [Contributing](#-contributing)
- [License](#-license)
- [Support](#-support)

## 🛠️ Development

### Traditional Development Setup

For development within the AgentFramework repository:

### 1. Installation

```bash
# Clone the repository
git clone <your-repository-url>
cd AgentFramework

# Install dependencies
uv venv
uv pip install -e .[dev]
```

### 2. Configuration

```bash
# Copy configuration template
cp env-template.txt .env

# Edit .env with your API keys
```

**Minimal .env setup:**
```env
# At least one API key required
OPENAI_API_KEY=sk-your-openai-key-here
GEMINI_API_KEY=your-gemini-api-key-here

# Set default model
DEFAULT_MODEL=gpt-4

# Authentication (optional - set to true to enable)
REQUIRE_AUTH=false
BASIC_AUTH_USERNAME=admin
BASIC_AUTH_PASSWORD=password
API_KEYS=sk-your-secure-api-key-123
```

### 3. Start the Server

**Option A: Using convenience function (recommended for external projects)**
```python
# In your agent file
from agent_framework import create_basic_agent_server
create_basic_agent_server(MyAgent, port=8000)
```

**Option B: Traditional method**
```bash
# Start the development server
uv run python agent.py

# Or using uvicorn directly
export AGENT_CLASS_PATH="agent:Agent"
uvicorn server:app --reload --host 0.0.0.0 --port 8000
```

### 4. Test the Agent

Open your browser to `http://localhost:8000/testapp` or make API calls:

```bash
# Without authentication (REQUIRE_AUTH=false)
curl -X POST http://localhost:8000/message \
  -H "Content-Type: application/json" \
  -d '{"query": "Hello, how are you?"}'

# With API Key authentication (REQUIRE_AUTH=true)
curl -X POST http://localhost:8000/message \
  -H "Content-Type: application/json" \
  -H "X-API-Key: sk-your-secure-api-key-123" \
  -d '{"query": "Hello, how are you?"}'

# With Basic authentication (REQUIRE_AUTH=true)
curl -u admin:password -X POST http://localhost:8000/message \
  -H "Content-Type: application/json" \
  -d '{"query": "Hello, how are you?"}'
```

### Project Structure

```
AgentFramework/
├── agent_framework/             # Main framework package
│   ├── __init__.py             # Library exports and convenience functions
│   ├── agent_interface.py      # Abstract agent interface
│   ├── base_agent.py          # AutoGen-based agent implementation
│   ├── server.py              # FastAPI server
│   ├── model_config.py        # Multi-provider configuration
│   ├── model_clients.py       # Model client factory
│   └── session_storage.py     # Session storage implementations
├── examples/                   # Usage examples
├── docs/                      # Documentation
├── test_app.html             # Web interface
├── env-template.txt          # Configuration template
└── pyproject.toml           # Package configuration
```

### Creating Custom Agents

1. **Inherit from AgentInterface:**

```python
from agent_framework import AgentInterface, StructuredAgentInput, StructuredAgentOutput

class MyCustomAgent(AgentInterface):
    async def handle_message(self, session_id: str, agent_input: StructuredAgentInput) -> StructuredAgentOutput:
        # Implement your logic here
        pass
    
    async def handle_message_stream(self, session_id: str, agent_input: StructuredAgentInput):
        # Implement streaming logic
        pass
    
    async def get_metadata(self):
        return {
            "name": "My Custom Agent",
            "description": "A custom agent implementation",
            "capabilities": {"streaming": True}
        }
    
    def get_system_prompt(self) -> Optional[str]:
        return "Your custom system prompt here..."
```

2. **Start the server:**

```python
from agent_framework import create_basic_agent_server
create_basic_agent_server(MyCustomAgent, port=8000)
```

### Testing

The project includes a comprehensive test suite built with `pytest`. The tests are located in the `tests/` directory and are configured to run in a self-contained environment.

For detailed instructions on how to set up the test environment and run the tests, please refer to the README file inside the test directory:

[**Agent Framework Test Suite Guide**](tests/README.md)

A brief overview of the steps:
1.  Navigate to the test directory: `cd tests`
2.  Create a virtual environment: `uv venv`
3.  Activate it: `source .venv/bin/activate`
4.  Install dependencies: `uv pip install -e .. && uv pip install -r requirements.txt`
5.  Run the tests: `pytest`

### Debug Logging

Set debug logging to see detailed system prompt and configuration information:

```bash
export AGENT_LOG_LEVEL=DEBUG
uv run python agent.py
```

Debug logs include:
- Model configuration loading and validation
- System prompt handling and persistence
- Agent configuration merging and application
- Provider selection and parameter filtering
- Client creation and model routing

## ⚙️ Configuration

### Multi-Provider Setup

The framework automatically routes requests to the appropriate AI provider based on the model name:

```env
# === API Keys ===
OPENAI_API_KEY=sk-your-openai-key-here
GEMINI_API_KEY=your-gemini-api-key-here

# === Default Model ===
DEFAULT_MODEL=gpt-4

# === Model Lists (Optional) ===
OPENAI_MODELS=gpt-4,gpt-4-turbo,gpt-4o,gpt-3.5-turbo,o1-preview,o1-mini
GEMINI_MODELS=gemini-1.5-pro,gemini-1.5-flash,gemini-2.0-flash-exp,gemini-pro

# === Provider Defaults ===
FALLBACK_PROVIDER=openai
OPENAI_DEFAULT_TEMPERATURE=0.7
GEMINI_DEFAULT_TEMPERATURE=0.7
```

### Session Storage Configuration

Configure persistent session storage (optional):

```env
# === Session Storage ===
# Use "memory" (default) for in-memory storage or "mongodb" for persistent storage
SESSION_STORAGE_TYPE=memory

# MongoDB configuration (only required when SESSION_STORAGE_TYPE=mongodb)
MONGODB_CONNECTION_STRING=mongodb://localhost:27017
MONGODB_DATABASE_NAME=agent_sessions
MONGODB_COLLECTION_NAME=sessions
```

For detailed MongoDB setup and configuration, see the [MongoDB Session Storage Guide](docs/mongodb_session_storage.md).

### Configuration Validation

Test your configuration:

```bash
# Validate configuration
uv run python test_multi_provider.py

# Check specific model support
curl http://localhost:8000/config/validate/gpt-4
```

## 📚 API Reference

### Core Endpoints

#### Send Message
Send a message to the agent and receive a complete response.

**Endpoint:** `POST /message`

**Request Body:**
```json
{
  "query": "Your message here",
  "parts": [],
  "system_prompt": "Optional custom system prompt",
  "agent_config": {
    "temperature": 0.8,
    "max_tokens": 1000,
    "model_selection": "gpt-4"
  },
  "session_id": "optional-session-id",
  "correlation_id": "optional-correlation-id-for-linking-sessions"
}
```

**Response:**
```json
{
  "response_text": "Agent's response",
  "parts": [
    {
      "type": "text",
      "text": "Agent's response"
    }
  ],
  "session_id": "generated-or-provided-session-id",
  "user_id": "user1",
  "correlation_id": "correlation-id-if-provided",
  "conversation_id": "unique-id-for-this-exchange"
}
```

#### Session Workflow (NEW)

**Initialize Session:** `POST /init`
```json
{
  "user_id": "string",           // required
  "correlation_id": "string",    // optional
  "session_id": "string",        // optional (auto-generated if not provided)
  "data": { ... },               // optional
  "configuration": {             // required
    "system_prompt": "string",
    "model_name": "string",
    "model_config": {
      "temperature": 0.7,
      "token_limit": 1000
    }
  }
}
```

Initializes a new chat session with immutable configuration. Must be called before any chat interactions. Returns the session configuration and generated session ID if not provided.

**End Session:** `POST /end`
```json
{
  "session_id": "string"
}
```

Closes a session and prevents further interactions. Persists final session state and locks feedback system.

**Submit Message Feedback:** `POST /feedback/message`
```json
{
  "session_id": "string",
  "message_id": "string",
  "feedback": "up" | "down"
}
```

Submit thumbs up/down feedback for a specific message. Can only be submitted once per message.

**Submit/Update Session Flag:** `POST|PUT /feedback/flag`
```json
{
  "session_id": "string",
  "flag_message": "string"
}
```

Submit or update a session-level flag message. Editable while session is active, locked after session ends.

#### Session Management

**List Sessions:** `GET /sessions`
```bash
curl http://localhost:8000/sessions
# Response: ["session1", "session2", ...]
```

**Get History:** `GET /sessions/{session_id}/history`
```bash
curl http://localhost:8000/sessions/abc123/history
```

**Find Sessions by Correlation ID:** `GET /sessions/by-correlation/{correlation_id}`
```bash
curl http://localhost:8000/sessions/by-correlation/task-123
# Response: [{"user_id": "user1", "session_id": "abc123", "correlation_id": "task-123"}]
```

### Correlation & Conversation Tracking

The framework provides advanced tracking capabilities for multi-agent workflows and detailed conversation analytics.

#### Correlation ID Support

**Purpose**: Link multiple sessions across different agents that are part of the same larger task or workflow.

**Usage**:
```python
# Start a task with correlation ID
response1 = client.send_message(
    "Analyze this data set",
    correlation_id="data-analysis-task-001"
)

# Continue task in another session/agent with same correlation ID
response2 = client.send_message(
    "Generate visualizations for the analysis",
    correlation_id="data-analysis-task-001"  # Same correlation ID
)

# Find all sessions related to this task
sessions = requests.get("/sessions/by-correlation/data-analysis-task-001")
```

**Key Features**:
- **Optional field**: Can be set when sending messages or creating sessions
- **Persistent**: Correlation ID is maintained throughout the session lifecycle
- **Cross-agent**: Multiple agents can share the same correlation ID
- **Searchable**: Query all sessions by correlation ID

#### Conversation ID Support

**Purpose**: Track individual message exchanges (request/reply pairs) within sessions for detailed analytics and debugging.

**Key Features**:
- **Automatic generation**: Each request/reply pair gets a unique conversation ID
- **Shared between request/reply**: User message and agent response share the same conversation ID
- **Database-ready**: Designed for storing individual exchanges in databases
- **Analytics-friendly**: Enables detailed conversation flow analysis

**Example Response with IDs**:
```json
{
  "response_text": "Here's the analysis...",
  "session_id": "session-abc-123",
  "user_id": "data-scientist-1",
  "correlation_id": "data-analysis-task-001",
  "conversation_id": "conv-uuid-456-789"
}
```

#### Manager Agent Coordination

These features enable sophisticated multi-agent workflows:

```python
class ManagerAgent:
    def __init__(self):
        self.correlation_id = f"task-{uuid.uuid4()}"
    
    async def coordinate_task(self, task_description):
        # Step 1: Data analysis agent
        analysis_response = await self.send_to_agent(
            "data-agent", 
            f"Analyze: {task_description}",
            correlation_id=self.correlation_id
        )
        
        # Step 2: Visualization agent
        viz_response = await self.send_to_agent(
            "viz-agent",
            f"Create charts for: {analysis_response}",
            correlation_id=self.correlation_id
        )
        
        # Step 3: Find all related sessions
        related_sessions = await self.get_sessions_by_correlation(self.correlation_id)
        
        return {
            "task_id": self.correlation_id,
            "sessions": related_sessions,
            "final_result": viz_response
        }
```

#### Web Interface Features

The test application includes full support for correlation tracking:

- **Correlation ID Input**: Set correlation IDs when sending messages
- **Session Finder**: Search for all sessions sharing a correlation ID
- **ID Display**: Shows correlation and conversation IDs in chat history
- **Visual Indicators**: Clear display of tracking information

#### Configuration Endpoints

**Get Model Configuration:** `GET /config/models`
```json
{
  "default_model": "gpt-4",
  "configuration_status": {
    "valid": true,
    "warnings": [],
    "errors": []
  },
  "supported_models": {
    "openai": ["gpt-4", "gpt-3.5-turbo"],
    "gemini": ["gemini-1.5-pro", "gemini-pro"]
  },
  "supported_providers": {
    "openai": true,
    "gemini": true
  }
}
```

**Validate Model:** `GET /config/validate/{model_name}`
```json
{
  "model": "gpt-4",
  "provider": "openai",
  "supported": true,
  "api_key_configured": true,
  "client_available": true,
  "issues": []
}
```

**Get System Prompt:** `GET /system-prompt`
```json
{
  "system_prompt": "You are a helpful AI assistant that helps users accomplish their tasks efficiently..."
}
```

Returns the default system prompt configured for the agent. Returns 404 if no system prompt is configured.

**Response (404 if not configured):**
```json
{
  "detail": "System prompt not configured"
}
```

### Agent Configuration Parameters

| Parameter | Type | Range | Description | Providers |
|-----------|------|-------|-------------|-----------|
| `temperature` | float | 0.0-2.0 | Controls randomness | OpenAI, Gemini |
| `max_tokens` | integer | 1+ | Maximum response tokens | OpenAI, Gemini |
| `top_p` | float | 0.0-1.0 | Nucleus sampling | OpenAI, Gemini |
| `frequency_penalty` | float | -2.0-2.0 | Reduce frequent tokens | OpenAI only |
| `presence_penalty` | float | -2.0-2.0 | Reduce any repetition | OpenAI only |
| `stop_sequences` | array | - | Custom stop sequences | OpenAI, Gemini |
| `timeout` | integer | 1+ | Request timeout (seconds) | OpenAI, Gemini |
| `max_retries` | integer | 0+ | Retry attempts | OpenAI, Gemini |
| `model_selection` | string | - | Override model for session | OpenAI, Gemini |

## 💻 Client Examples

### Python Client

```python
import requests
import json

class AgentClient:
    def __init__(self, base_url="http://localhost:8000"):
        self.base_url = base_url
        self.session = requests.Session()
        # Add basic auth if required
        self.session.auth = ("admin", "password")
    
    def send_message(self, message, session_id=None, correlation_id=None):
        """Send a message and get complete response."""
        payload = {
            "query": message,
            "parts": []
        }
        
        if session_id:
            payload["session_id"] = session_id
        if correlation_id:
            payload["correlation_id"] = correlation_id
        
        response = self.session.post(
            f"{self.base_url}/message",
            json=payload
        )
        response.raise_for_status()
        return response.json()
    
    def init_session(self, user_id, configuration, correlation_id=None, session_id=None, data=None):
        """Initialize a new session with configuration."""
        payload = {
            "user_id": user_id,
            "configuration": configuration
        }
        
        if correlation_id:
            payload["correlation_id"] = correlation_id
        if session_id:
            payload["session_id"] = session_id
        if data:
            payload["data"] = data
        
        response = self.session.post(
            f"{self.base_url}/init",
            json=payload
        )
        response.raise_for_status()
        return response.json()
    
    def end_session(self, session_id):
        """End a session."""
        response = self.session.post(
            f"{self.base_url}/end",
            json={"session_id": session_id}
        )
        response.raise_for_status()
        return response.ok
    
    def submit_feedback(self, session_id, message_id, feedback):
        """Submit feedback for a message."""
        response = self.session.post(
            f"{self.base_url}/feedback/message",
            json={
                "session_id": session_id,
                "message_id": message_id,
                "feedback": feedback
            }
        )
        response.raise_for_status()
        return response.ok
    
    def get_model_config(self):
        """Get available models and configuration."""
        response = self.session.get(f"{self.base_url}/config/models")
        response.raise_for_status()
        return response.json()

# Usage example
client = AgentClient()

# Initialize session with configuration
session_data = client.init_session(
    user_id="user123",
    configuration={
        "system_prompt": "You are a creative writing assistant",
        "model_name": "gpt-4",
        "model_config": {
            "temperature": 1.2,
            "token_limit": 500
        }
    },
    correlation_id="creative-writing-session-001"
)

session_id = session_data["session_id"]

# Send messages using the initialized session
response = client.send_message(
    "Write a creative story about space exploration",
    session_id=session_id
)
print(response["response_text"])

# Submit feedback on the response
client.submit_feedback(session_id, response["conversation_id"], "up")

# Continue the conversation
response2 = client.send_message("Add more details about the characters", session_id=session_id)
print(response2["response_text"])

# End session when done
client.end_session(session_id)
```

### JavaScript Client

```javascript
class AgentClient {
    constructor(baseUrl = 'http://localhost:8000') {
        this.baseUrl = baseUrl;
        this.auth = btoa('admin:password'); // Basic auth
    }
    
    async sendMessage(message, options = {}) {
        const payload = {
            query: message,
            parts: [],
            ...options
        };
        
        const response = await fetch(`${this.baseUrl}/message`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Basic ${this.auth}`
            },
            body: JSON.stringify(payload)
        });
        
        if (!response.ok) {
            throw new Error(`HTTP ${response.status}: ${response.statusText}`);
        }
        
        return response.json();
    }
    
    async initSession(userId, configuration, options = {}) {
        const payload = {
            user_id: userId,
            configuration,
            ...options
        };
        
        const response = await fetch(`${this.baseUrl}/init`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Basic ${this.auth}`
            },
            body: JSON.stringify(payload)
        });
        
        if (!response.ok) {
            throw new Error(`HTTP ${response.status}: ${response.statusText}`);
        }
        
        return response.json();
    }
    
    async endSession(sessionId) {
        const response = await fetch(`${this.baseUrl}/end`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Basic ${this.auth}`
            },
            body: JSON.stringify({ session_id: sessionId })
        });
        
        if (!response.ok) {
            throw new Error(`HTTP ${response.status}: ${response.statusText}`);
        }
        
        return response.ok;
    }
    
    async submitFeedback(sessionId, messageId, feedback) {
        const response = await fetch(`${this.baseUrl}/feedback/message`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Basic ${this.auth}`
            },
            body: JSON.stringify({
                session_id: sessionId,
                message_id: messageId,
                feedback
            })
        });
        
        return response.ok;
    }
    
    async getModelConfig() {
        const response = await fetch(`${this.baseUrl}/config/models`, {
            headers: { 'Authorization': `Basic ${this.auth}` }
        });
        return response.json();
    }
}

// Usage example
const client = new AgentClient();

// Initialize session with configuration
const sessionInit = await client.initSession('user123', {
    system_prompt: 'You are a helpful coding assistant',
    model_name: 'gpt-4',
    model_config: {
        temperature: 0.7,
        token_limit: 1000
    }
}, {
    correlation_id: 'coding-help-001'
});

// Send messages using the initialized session
const response = await client.sendMessage('Help me debug this Python code', {
    session_id: sessionInit.session_id
});
console.log(response.response_text);

// Submit feedback
await client.submitFeedback(sessionInit.session_id, response.conversation_id, 'up');

// End session when done
await client.endSession(sessionInit.session_id);
```

### curl Examples

```bash
# Basic message with correlation ID
curl -X POST http://localhost:8000/message \
  -u admin:password \
  -H "Content-Type: application/json" \
  -d '{
    "query": "Hello, world!",
    "correlation_id": "greeting-task-001",
    "agent_config": {
      "temperature": 0.8,
      "model_selection": "gpt-4"
    }
  }'

# Initialize session
curl -X POST http://localhost:8000/init \
  -u admin:password \
  -H "Content-Type: application/json" \
  -d '{
    "user_id": "user123",
    "correlation_id": "poetry-session-001",
    "configuration": {
      "system_prompt": "You are a talented poet",
      "model_name": "gpt-4",
      "model_config": {
        "temperature": 1.5,
        "token_limit": 200
      }
    }
  }'

# Submit feedback for a message
curl -X POST http://localhost:8000/feedback/message \
  -u admin:password \
  -H "Content-Type: application/json" \
  -d '{
    "session_id": "session-123",
    "message_id": "msg-456",
    "feedback": "up"
  }'

# End session
curl -X POST http://localhost:8000/end \
  -u admin:password \
  -H "Content-Type: application/json" \
  -d '{
    "session_id": "session-123"
  }'

# Get model configuration
curl http://localhost:8000/config/models -u admin:password

# Validate model support
curl http://localhost:8000/config/validate/gemini-1.5-pro -u admin:password

# Get system prompt
curl http://localhost:8000/system-prompt -u admin:password

# Find sessions by correlation ID
curl http://localhost:8000/sessions/by-correlation/greeting-task-001 -u admin:password
```

## 🌐 Web Interface

Access the built-in web interface at `http://localhost:8000/testapp`

### Features:
- **Model Selection**: Dropdown with all available models
- **System Prompt Management**: 
  - Dedicated textarea for custom prompts
  - Auto-loads default system prompt from server
  - Session-specific prompt persistence
  - Reset to default functionality
  - Manual reload from server option
- **Advanced Configuration**: Collapsible panel with all parameters
- **Parameter Validation**: Real-time validation with visual feedback
- **Provider Awareness**: Disables unsupported parameters (e.g., frequency_penalty for Gemini)
- **Session Management**: Create, load, and manage conversation sessions with structured workflow
- **Session Initialization**: Configure sessions with immutable system prompts and model settings
- **User Feedback**: Thumbs up/down feedback and session-level flags
- **Media Detection**: Automatic detection and display of generated images/videos
- **Correlation Tracking**: 
  - Set correlation IDs to link sessions across agents
  - Search for sessions by correlation ID
  - Visual display of correlation and conversation IDs
  - Manager agent coordination support

### Configuration Presets:
- **Creative**: High temperature, relaxed parameters for creative tasks
- **Precise**: Low temperature, focused parameters for analytical tasks
- **Custom**: Manual parameter adjustment

## 🔧 Advanced Usage

### System Prompt Configuration

The framework supports configurable system prompts both at the server level and per-session:

#### Server-Level System Prompt
Agents can provide a default system prompt via the `get_system_prompt()` method:

```python
class MyAgent(AgentInterface):
    def get_system_prompt(self) -> Optional[str]:
        return """
        You are a helpful coding assistant specializing in Python.
        Always provide:
        1. Working code examples
        2. Clear explanations
        3. Best practices
        4. Error handling
        """
```

#### Accessing System Prompt via API
```python
# Get the default system prompt from server
response = requests.get("http://localhost:8000/system-prompt")
if response.status_code == 200:
    system_prompt = response.json()["system_prompt"]
else:
    print("No system prompt configured")
```

#### Per-Session System Prompts
```python
# Set system prompt for specific use case
custom_prompt = """
You are a creative writing assistant.
Focus on storytelling and narrative structure.
"""

response = client.send_message(
    "Help me write a short story",
    system_prompt=custom_prompt
)
```

#### Web Interface System Prompt Management
The web interface provides comprehensive system prompt management:
- **Auto-loading**: Default system prompt loads automatically on new sessions
- **Session persistence**: Each session remembers its custom system prompt
- **Reset functionality**: "🔄 Reset to Default" button restores server default
- **Manual reload**: Refresh system prompt from server without losing session data

### Model-Specific Configuration

```python
# OpenAI-specific configuration
openai_config = {
    "model_selection": "gpt-4",
    "temperature": 0.7,
    "frequency_penalty": 0.5,  # OpenAI only
    "presence_penalty": 0.3    # OpenAI only
}

# Gemini-specific configuration  
gemini_config = {
    "model_selection": "gemini-1.5-pro",
    "temperature": 0.8,
    "top_p": 0.9,
    "max_tokens": 1000
    # Note: frequency_penalty not supported by Gemini
}
```

### Session Persistence

```python
# Start conversation with custom settings
response1 = client.send_message(
    "Let's start a coding session",
    system_prompt="You are my coding pair programming partner",
    config={"temperature": 0.3}
)

session_id = response1["session_id"]

# Continue conversation - settings persist
response2 = client.send_message(
    "Help me debug this function",
    session_id=session_id
)

# Override settings for this message only
response3 = client.send_message(
    "Now be creative and suggest alternatives", 
    session_id=session_id,
    config={"temperature": 1.5}  # Temporary override
)
```

### Multi-Modal Support

```python
# Send image with message
payload = {
    "query": "What's in this image?",
    "parts": [
        {
            "type": "image_url",
            "image_url": {"url": "data:image/jpeg;base64,/9j/4AAQ..."}
        }
    ]
}
```

## 🔒 Authentication

The framework supports two authentication methods that can be used simultaneously:

### 1. Basic Authentication (Username/Password)

HTTP Basic Authentication using username and password credentials.

**Configuration:**
```env
# Enable authentication
REQUIRE_AUTH=true

# Basic Auth credentials
BASIC_AUTH_USERNAME=admin
BASIC_AUTH_PASSWORD=your-secure-password
```

**Usage Examples:**

```bash
# cURL with Basic Auth
curl -u admin:password http://localhost:8000/message \
  -H "Content-Type: application/json" \
  -d '{"query": "Hello!"}'

# Python requests
import requests
response = requests.post(
    "http://localhost:8000/message",
    json={"query": "Hello!"},
    auth=("admin", "password")
)
```

### 2. API Key Authentication

More secure option for API clients using bearer tokens or X-API-Key headers.

**Configuration:**
```env
# Enable authentication
REQUIRE_AUTH=true

# API Keys (comma-separated list of valid keys)
API_KEYS=sk-your-secure-key-123,ak-another-api-key-456,my-client-api-key-789
```

**Usage Examples:**

```bash
# cURL with Bearer Token
curl -H "Authorization: Bearer sk-your-secure-key-123" \
  http://localhost:8000/message \
  -H "Content-Type: application/json" \
  -d '{"query": "Hello!"}'

# cURL with X-API-Key Header
curl -H "X-API-Key: sk-your-secure-key-123" \
  http://localhost:8000/message \
  -H "Content-Type: application/json" \
  -d '{"query": "Hello!"}'

# Python requests with Bearer Token
import requests
headers = {
    "Authorization": "Bearer sk-your-secure-key-123",
    "Content-Type": "application/json"
}
response = requests.post(
    "http://localhost:8000/message",
    json={"query": "Hello!"},
    headers=headers
)

# Python requests with X-API-Key
headers = {
    "X-API-Key": "sk-your-secure-key-123",
    "Content-Type": "application/json"
}
response = requests.post(
    "http://localhost:8000/message",
    json={"query": "Hello!"},
    headers=headers
)
```

### Authentication Priority

The framework tries authentication methods in this order:
1. **API Key via Bearer Token** (`Authorization: Bearer <key>`)
2. **API Key via X-API-Key Header** (`X-API-Key: <key>`)
3. **Basic Authentication** (username/password)

### Python Client Library Support

```python
from AgentClient import AgentClient

# Using Basic Auth
client = AgentClient("http://localhost:8000")
client.session.auth = ("admin", "password")

# Using API Key
client = AgentClient("http://localhost:8000")
client.session.headers.update({"X-API-Key": "sk-your-secure-key-123"})

# Send authenticated request
response = client.send_message("Hello, authenticated world!")
```

### Web Interface Authentication

The web interface (`/testapp`) supports both authentication methods. Update the JavaScript client:

```javascript
// Basic Auth
this.auth = btoa('admin:password');
headers['Authorization'] = `Basic ${this.auth}`;

// API Key
headers['X-API-Key'] = 'sk-your-secure-key-123';
```

### Security Best Practices

1. **Use Strong API Keys**: Generate cryptographically secure random keys
2. **Rotate Keys Regularly**: Update API keys periodically
3. **Environment Variables**: Never hardcode credentials in source code
4. **HTTPS Only**: Always use HTTPS in production to protect credentials
5. **Minimize Key Scope**: Use different keys for different applications/users

**Generate Secure API Keys:**
```bash
# Generate a secure API key (32 bytes, base64 encoded)
python -c "import secrets, base64; print('sk-' + base64.urlsafe_b64encode(secrets.token_bytes(32)).decode().rstrip('='))"

# Or use openssl
openssl rand -base64 32 | sed 's/^/sk-/'
```

### Disable Authentication

To disable authentication completely:

```env
REQUIRE_AUTH=false
```

When disabled, all endpoints are publicly accessible without any authentication.

## 📝 Contributing

1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Add tests for new functionality
5. Submit a pull request

## 📄 License

[Your License Here]

## 🤝 Support

- **Documentation**: This README and inline code comments
- **Examples**: See `test_*.py` files for usage examples
- **Issues**: Report bugs and feature requests via GitHub Issues

---

**Quick Links:**
- [Web Interface](http://localhost:8000/testapp) - Interactive testing
- [API Documentation](http://localhost:8000/docs) - OpenAPI/Swagger docs
- [Configuration Test](http://localhost:8000/config/models) - Validate setup
