diff --git a/docs-requirements.txt b/docs-requirements.txt new file mode 100644 index 0000000..396ed2c --- /dev/null +++ b/docs-requirements.txt @@ -0,0 +1,16 @@ +# FastGPT Python SDK - Documentation Requirements + +# Install with: +# pip install -r docs-requirements.txt + +# Core documentation tools +mkdocs>=1.5.0 +mkdocs-material>=9.5.0 + +# API documentation generation +mkdocstrings[python]>=0.24.0 + +# Optional: For additional features +# mkdocs-awesome-pages-plugin # For better page organization +# mkdocs-minify-plugin # For minifying HTML output +# mkdocs-redirects # For setting up redirects diff --git a/docs/advanced/detail_mode.md b/docs/advanced/detail_mode.md new file mode 100644 index 0000000..ef3ac3a --- /dev/null +++ b/docs/advanced/detail_mode.md @@ -0,0 +1,343 @@ +# Detail Mode + +Learn how to use FastGPT's detail mode to get comprehensive execution data for your requests. + +## What is Detail Mode? + +When `detail=True`, FastGPT returns extensive execution information including: + +- Module-by-module execution details +- Token usage per module +- Execution time for each node +- Knowledge base citations +- Complete message contexts +- Cost information + +## Enabling Detail Mode + +```python +from fastgpt_client import ChatClient + +with ChatClient(api_key="fastgpt-xxxxx") as client: + response = client.create_chat_completion( + messages=[{"role": "user", "content": "Explain AI"}], + detail=True, # Enable detail mode + stream=False + ) + result = response.json() +``` + +## Response Structure with Detail Mode + +### Basic Response (detail=False) + +```json +{ + "id": "chatcmpl-xxx", + "choices": [{ + "message": {"content": "AI is..."} + }], + "usage": { + "prompt_tokens": 10, + "completion_tokens": 50, + "total_tokens": 60 + } +} +``` + +### Detailed Response (detail=True) + +```json +{ + "id": "chatcmpl-xxx", + "choices": [{ + "message": {"content": "AI is..."} + }], + "usage": { + "prompt_tokens": 10, + "completion_tokens": 50, + "total_tokens": 60 + }, + "responseData": [ + { + "moduleName": "Chat Node", + "moduleType": "chatNode", + "tokens": 60, + "price": 0.0012, + "runningTime": 1.5, + "quoteList": [ + { + "sourceId": "kb_123", + "sourceName": "AI Knowledge Base", + "text": "AI stands for Artificial Intelligence..." + } + ], + "completeMessages": [...] + }, + { + "moduleName": "Dataset Search", + "moduleType": "datasetSearchNode", + "tokens": 20, + "price": 0.0004, + "runningTime": 0.5 + } + ] +} +``` + +## Parsing Detailed Responses + +```python +def parse_detail_response(response_data: dict): + """Parse and display detailed execution data.""" + + # Basic response info + content = response_data['choices'][0]['message']['content'] + usage = response_data.get('usage', {}) + print(f"Response: {content[:100]}...") + print(f"Total Tokens: {usage.get('total_tokens', 0)}") + + # Detailed execution data + response_details = response_data.get('responseData', []) + + if response_details: + print("\n=== Execution Details ===") + + for module in response_details: + module_name = module.get('moduleName', 'Unknown') + module_type = module.get('moduleType', 'Unknown') + tokens = module.get('tokens', 0) + price = module.get('price', 0) + running_time = module.get('runningTime', 0) + + print(f"\nModule: {module_name} ({module_type})") + print(f" Tokens: {tokens}") + print(f" Price: ${price:.6f}") + print(f" Runtime: {running_time}s") + + # Knowledge base citations + quote_list = module.get('quoteList', []) + if quote_list: + print(f" Citations:") + for quote in quote_list: + source = quote.get('sourceName', 'Unknown') + text = quote.get('text', '')[:100] + print(f" - {source}: {text}...") + + +# Usage +with ChatClient(api_key="fastgpt-xxxxx") as client: + response = client.create_chat_completion( + messages=[{"role": "user", "content": "What is AI?"}], + detail=True, + stream=False + ) + result = response.json() + parse_detail_response(result) +``` + +## Streaming with Detail Mode + +```python +import json +from fastgpt_client import ChatClient + +def stream_with_detail(client, messages): + """Stream with detail mode events.""" + + response = client.create_chat_completion( + messages=messages, + detail=True, + stream=True + ) + + modules = [] + + for line in response.iter_lines(): + if line.startswith("event:flowNodeStatus"): + # Node status updates + data = json.loads(line[6:].split('data:', 1)[1]) + status = data.get('status') + node = data.get('moduleName') + print(f"[{status.upper()}] {node}") + + elif line.startswith("event:flowResponses"): + # Complete module execution data + data = json.loads(line[6:].split('data:', 1)[1]) + modules.append(data) + + elif line.startswith("data:"): + # Standard response chunks + data = line[5:].strip() + if data and data != "[DONE]": + chunk = json.loads(data) + if "choices" in chunk and chunk["choices"]: + delta = chunk["choices"][0].get("delta", {}) + content = delta.get("content", "") + if content: + print(content, end="", flush=True) + + print("\n\n=== Module Summary ===") + for module in modules: + print(f"{module.get('moduleName')}: {module.get('tokens')} tokens") + + +# Usage +with ChatClient(api_key="fastgpt-xxxxx") as client: + stream_with_detail( + client, + [{"role": "user", "content": "Explain quantum computing"}] + ) +``` + +## Extracting Knowledge Base Citations + +```python +def get_citations(response_data: dict) -> list[dict]: + """Extract knowledge base citations from detailed response.""" + + citations = [] + response_details = response_data.get('responseData', []) + + for module in response_details: + quote_list = module.get('quoteList', []) + for quote in quote_list: + citations.append({ + 'source': quote.get('sourceName', 'Unknown'), + 'source_id': quote.get('sourceId', ''), + 'text': quote.get('text', ''), + 'module': module.get('moduleName', 'Unknown') + }) + + return citations + + +# Usage +with ChatClient(api_key="fastgpt-xxxxx") as client: + response = client.create_chat_completion( + messages=[{"role": "user", "content": "What is machine learning?"}], + detail=True, + stream=False + ) + result = response.json() + + citations = get_citations(result) + print(f"Found {len(citations)} citations:") + for i, citation in enumerate(citations, 1): + print(f"\n{i}. {citation['source']}") + print(f" {citation['text'][:150]}...") +``` + +## Calculating Costs + +```python +def calculate_costs(response_data: dict) -> dict: + """Calculate total and per-module costs.""" + + total_cost = 0 + total_tokens = 0 + module_costs = [] + + response_details = response_data.get('responseData', []) + + for module in response_details: + cost = module.get('price', 0) + tokens = module.get('tokens', 0) + module_name = module.get('moduleName', 'Unknown') + + total_cost += cost + total_tokens += tokens + + module_costs.append({ + 'module': module_name, + 'cost': cost, + 'tokens': tokens + }) + + return { + 'total_cost': total_cost, + 'total_tokens': total_tokens, + 'modules': module_costs + } + + +# Usage +with ChatClient(api_key="fastgpt-xxxxx") as client: + response = client.create_chat_completion( + messages=[{"role": "user", "content": "Tell me about AI"}], + detail=True, + stream=False + ) + result = response.json() + + costs = calculate_costs(result) + print(f"Total Cost: ${costs['total_cost']:.6f}") + print(f"Total Tokens: {costs['total_tokens']}") + print("\nPer-Module Costs:") + for module in costs['modules']: + print(f" {module['module']}: ${module['cost']:.6f} ({module['tokens']} tokens)") +``` + +## Analyzing Execution Time + +```python +def analyze_performance(response_data: dict) -> dict: + """Analyze module execution performance.""" + + total_time = 0 + modules = [] + + for module in response_data.get('responseData', []): + runtime = module.get('runningTime', 0) + module_name = module.get('moduleName', 'Unknown') + + total_time += runtime + modules.append({ + 'module': module_name, + 'runtime': runtime + }) + + # Sort by runtime + modules.sort(key=lambda x: x['runtime'], reverse=True) + + return { + 'total_time': total_time, + 'modules': modules + } + + +# Usage +with ChatClient(api_key="fastgpt-xxxxx") as client: + response = client.create_chat_completion( + messages=[{"role": "user", "content": "Analyze this data"}], + detail=True, + stream=False + ) + result = response.json() + + perf = analyze_performance(result) + print(f"Total Runtime: {perf['total_time']:.2f}s") + print("\nModule Execution Times:") + for module in perf['modules']: + print(f" {module['module']}: {module['runtime']:.2f}s") +``` + +## Use Cases + +1. **Debugging** - Identify slow or expensive modules +2. **Cost Optimization** - Track token usage and costs +3. **Transparency** - Show sources and reasoning to users +4. **Analytics** - Monitor application performance +5. **Compliance** - Track AI-generated content sources + +## Best Practices + +1. **Use selectively** - Detail mode adds overhead +2. **Cache results** - Store detailed data for analysis +3. **Monitor costs** - Track token usage over time +4. **Optimize workflows** - Use performance data to improve + +## See Also + +- [Streaming Events](streaming_events.md) - Real-time execution events +- [Record Detail API](../api/chat_client.md#get_record_detail) - Get details for past records diff --git a/docs/advanced/error_handling.md b/docs/advanced/error_handling.md new file mode 100644 index 0000000..b1a10c3 --- /dev/null +++ b/docs/advanced/error_handling.md @@ -0,0 +1,319 @@ +# Error Handling + +A comprehensive guide to handling errors in the FastGPT Python SDK. + +## Exception Types + +The SDK provides specific exceptions for different error scenarios: + +| Exception | Status Code | When to Use | +|-----------|-------------|-------------| +| `AuthenticationError` | 401 | Invalid API key | +| `RateLimitError` | 429 | Too many requests | +| `ValidationError` | 422 | Invalid parameters | +| `APIError` | 4xx/5xx | General API errors | + +## Basic Error Handling + +```python +from fastgpt_client import ChatClient +from fastgpt_client.exceptions import ( + APIError, + AuthenticationError, + RateLimitError, + ValidationError +) + +try: + with ChatClient(api_key="fastgpt-xxxxx") as client: + response = client.create_chat_completion( + messages=[{"role": "user", "content": "Hello"}], + stream=False + ) + response.raise_for_status() + result = response.json() + print(result['choices'][0]['message']['content']) + +except AuthenticationError: + print("Authentication failed. Check your API key.") + +except RateLimitError as e: + print(f"Rate limit exceeded. Retry after: {e.retry_after}") + +except ValidationError as e: + print(f"Invalid parameters: {e.message}") + +except APIError as e: + print(f"API error: {e.message}") +``` + +## Comprehensive Error Handler + +```python +from fastgpt_client import ChatClient +from fastgpt_client.exceptions import FastGPTError +import logging + +logging.basicConfig(level=logging.ERROR) +logger = logging.getLogger(__name__) + + +class ChatService: + """Chat service with comprehensive error handling.""" + + def __init__(self, api_key: str, base_url: str): + self.client = ChatClient(api_key=api_key, base_url=base_url) + + def send_message(self, message: str) -> str | None: + """Send a message with error handling.""" + try: + response = self.client.create_chat_completion( + messages=[{"role": "user", "content": message}], + stream=False + ) + response.raise_for_status() + result = response.json() + return result['choices'][0]['message']['content'] + + except AuthenticationError: + logger.error("Invalid API key") + return "Error: Authentication failed. Please check your API key." + + except RateLimitError as e: + logger.error(f"Rate limit exceeded: {e}") + wait_time = int(e.retry_after) if e.retry_after else 5 + return f"Error: Too many requests. Please wait {wait_time} seconds." + + except ValidationError as e: + logger.error(f"Validation error: {e}") + return f"Error: Invalid request - {e.message}" + + except APIError as e: + logger.error(f"API error: {e}") + return f"Error: Server error - {e.message}" + + except Exception as e: + logger.exception(f"Unexpected error: {e}") + return "Error: An unexpected error occurred." + + def close(self): + """Close the client.""" + self.client.close() +``` + +## Retry Logic + +### Simple Retry + +```python +import time +from fastgpt_client import ChatClient +from fastgpt_client.exceptions import RateLimitError + +def chat_with_retry(client, messages, max_retries=3): + """Retry chat completion on rate limit errors.""" + for attempt in range(max_retries): + try: + response = client.create_chat_completion( + messages=messages, + stream=False + ) + response.raise_for_status() + return response.json() + + except RateLimitError as e: + if attempt < max_retries - 1: + wait_time = int(e.retry_after) if e.retry_after else 5 + print(f"Rate limited. Waiting {wait_time} seconds...") + time.sleep(wait_time) + else: + raise +``` + +### Exponential Backoff + +```python +import time +from fastgpt_client import ChatClient +from fastgpt_client.exceptions import APIError + +def chat_with_backoff(client, messages, max_retries=5): + """Retry with exponential backoff.""" + base_delay = 1 # seconds + + for attempt in range(max_retries): + try: + response = client.create_chat_completion( + messages=messages, + stream=False + ) + response.raise_for_status() + return response.json() + + except APIError as e: + if attempt < max_retries - 1 and e.status_code >= 500: + # Exponential backoff for server errors + delay = base_delay * (2 ** attempt) + print(f"Server error. Retrying in {delay} seconds...") + time.sleep(delay) + else: + raise +``` + +## Streaming Error Handling + +```python +import json +from fastgpt_client import ChatClient +from fastgpt_client.exceptions import FastGPTError + +def stream_chat_safely(client, messages): + """Handle streaming with error recovery.""" + try: + response = client.create_chat_completion( + messages=messages, + stream=True + ) + + for line in response.iter_lines(): + try: + if line.startswith("data:"): + data = line[5:].strip() + if data and data != "[DONE]": + chunk = json.loads(data) + if "choices" in chunk and chunk["choices"]: + delta = chunk["choices"][0].get("delta", {}) + content = delta.get("content", "") + if content: + print(content, end="", flush=True) + except json.JSONDecodeError: + # Skip malformed JSON chunks + continue + + except FastGPTError as e: + print(f"\nStream error: {e}") +``` + +## Response Validation + +```python +def validate_response(response_data: dict) -> bool: + """Validate API response structure.""" + if "choices" not in response_data: + raise ValueError("Response missing 'choices' field") + + if not response_data["choices"]: + raise ValueError("Empty choices array") + + choice = response_data["choices"][0] + if "message" not in choice: + raise ValueError("Choice missing 'message' field") + + message = choice["message"] + if "content" not in message: + raise ValueError("Message missing 'content' field") + + return True + + +def safe_chat_completion(client, messages): + """Chat with response validation.""" + response = client.create_chat_completion( + messages=messages, + stream=False + ) + response.raise_for_status() + result = response.json() + + try: + validate_response(result) + return result['choices'][0]['message']['content'] + except ValueError as e: + print(f"Invalid response format: {e}") + return None +``` + +## Logging Errors + +```python +import logging +from fastgpt_client import ChatClient +from fastgpt_client.exceptions import FastGPTError + +# Configure logging +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' +) +logger = logging.getLogger(__name__) + +def log_errors(client, messages): + """Log errors with context.""" + try: + response = client.create_chat_completion( + messages=messages, + stream=False + ) + response.raise_for_status() + return response.json() + + except FastGPTError as e: + logger.error( + f"API Error: {type(e).__name__} - {e.message}", + extra={ + "status_code": e.status_code, + "response_data": e.response_data, + } + ) + raise +``` + +## Custom Exception Handler + +```python +from functools import wraps +from fastgpt_client.exceptions import FastGPTError + +def handle_fastgpt_errors(func): + """Decorator for handling FastGPT errors.""" + + @wraps(func) + def wrapper(*args, **kwargs): + try: + return func(*args, **kwargs) + except FastGPTError as e: + print(f"FastGPT Error: {e}") + return None + except Exception as e: + print(f"Unexpected Error: {e}") + return None + + return wrapper + + +@handle_fastgpt_errors +def send_message(client, message: str): + """Send message with automatic error handling.""" + response = client.create_chat_completion( + messages=[{"role": "user", "content": message}], + stream=False + ) + response.raise_for_status() + result = response.json() + return result['choices'][0]['message']['content'] +``` + +## Best Practices + +1. **Always use `raise_for_status()`** - Catches HTTP errors early +2. **Handle specific exceptions** - Use except blocks for known error types +3. **Log all errors** - Helps with debugging and monitoring +4. **Provide user feedback** - Show meaningful error messages +5. **Implement retries** - For transient errors (rate limits, server errors) +6. **Validate responses** - Ensure data structure is correct +7. **Use context managers** - Ensures proper cleanup + +## See Also + +- [Exceptions Reference](../api/exceptions.md) - Exception types and attributes +- [Rate Limiting](rate_limiting.md) - Handling rate limits effectively diff --git a/docs/advanced/rate_limiting.md b/docs/advanced/rate_limiting.md new file mode 100644 index 0000000..d40ce82 --- /dev/null +++ b/docs/advanced/rate_limiting.md @@ -0,0 +1,298 @@ +# Rate Limiting + +Understanding and handling rate limits in the FastGPT API. + +## Understanding Rate Limits + +FastGPT API may enforce rate limits to: + +- Prevent API abuse +- Ensure fair resource allocation +- Maintain system stability + +When you exceed the rate limit, you'll receive a `429 Too Many Requests` response. + +## RateLimitError + +The SDK raises `RateLimitError` when rate limits are exceeded: + +```python +from fastgpt_client import ChatClient +from fastgpt_client.exceptions import RateLimitError + +try: + response = client.create_chat_completion( + messages=[{"role": "user", "content": "Hello"}] + ) +except RateLimitError as e: + print(f"Rate limit exceeded!") + print(f"Status code: {e.status_code}") # 429 + print(f"Retry after: {e.retry_after}") # Suggested wait time +``` + +## Handling Rate Limits + +### 1. Simple Retry with Delay + +```python +import time +from fastgpt_client.exceptions import RateLimitError + +def chat_with_retry(client, messages, max_retries=3): + """Retry on rate limit with fixed delay.""" + for attempt in range(max_retries): + try: + response = client.create_chat_completion( + messages=messages, + stream=False + ) + response.raise_for_status() + return response.json() + + except RateLimitError as e: + if attempt < max_retries - 1: + # Use Retry-After header or default to 5 seconds + wait_time = int(e.retry_after) if e.retry_after else 5 + print(f"Rate limited. Waiting {wait_time} seconds...") + time.sleep(wait_time) + else: + print("Max retries exceeded") + raise +``` + +### 2. Exponential Backoff + +```python +import time + +def chat_with_backoff(client, messages, max_retries=5): + """Retry with exponential backoff.""" + base_delay = 1 # Start with 1 second + + for attempt in range(max_retries): + try: + response = client.create_chat_completion( + messages=messages, + stream=False + ) + response.raise_for_status() + return response.json() + + except RateLimitError as e: + if attempt < max_retries - 1: + # Exponential backoff with jitter + delay = base_delay * (2 ** attempt) + # Add jitter to avoid thundering herd + import random + jitter = random.uniform(0, 0.5 * delay) + wait_time = delay + jitter + + print(f"Rate limited. Waiting {wait_time:.1f} seconds...") + time.sleep(wait_time) + else: + raise +``` + +### 3. Async Retry with Backoff + +```python +import asyncio + +async def async_chat_with_retry(client, messages, max_retries=5): + """Async retry with exponential backoff.""" + base_delay = 1 + + for attempt in range(max_retries): + try: + response = await client.create_chat_completion( + messages=messages, + stream=False + ) + response.raise_for_status() + return response.json() + + except RateLimitError as e: + if attempt < max_retries - 1: + delay = base_delay * (2 ** attempt) + print(f"Rate limited. Waiting {delay} seconds...") + await asyncio.sleep(delay) + else: + raise +``` + +### 4. Rate Limiter Class + +```python +import time +from collections import deque +from threading import Lock + +class RateLimiter: + """Token bucket rate limiter.""" + + def __init__(self, rate: int, per: float = 60.0): + """ + Args: + rate: Number of requests allowed + per: Time period in seconds + """ + self.rate = rate + self.per = per + self.allowance = rate + self.last_check = time.time() + self.lock = Lock() + + def acquire(self, block: bool = True, timeout: float = None) -> bool: + """Acquire a token from the bucket.""" + with self.lock: + current = time.time() + time_passed = current - self.last_check + self.last_check = current + + # Refill bucket + self.allowance += time_passed * (self.rate / self.per) + + if self.allowance > self.rate: + self.allowance = self.rate + + if self.allowance < 1.0: + if not block: + return False + + # Calculate wait time + sleep_time = (1.0 - self.allowance) * (self.per / self.rate) + + if timeout is not None and sleep_time > timeout: + return False + + time.sleep(sleep_time) + self.allowance = 0.0 + else: + self.allowance -= 1.0 + + return True + + +# Usage +rate_limiter = RateLimiter(rate=10, per=60) # 10 requests per minute + +for i in range(15): + if rate_limiter.acquire(): + response = client.create_chat_completion( + messages=[{"role": "user", "content": f"Message {i}"}] + ) + print(f"Sent message {i}") + else: + print(f"Rate limited, skipping message {i}") +``` + +### 5. Decorator for Rate Limiting + +```python +import time +import functools +from fastgpt_client.exceptions import RateLimitError + +def rate_limit_retry(max_retries=3, base_delay=1): + """Decorator to handle rate limiting with retries.""" + + def decorator(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + for attempt in range(max_retries): + try: + return func(*args, **kwargs) + except RateLimitError as e: + if attempt < max_retries - 1: + delay = base_delay * (2 ** attempt) + wait_time = int(e.retry_after) if e.retry_after else delay + print(f"Rate limited. Waiting {wait_time} seconds...") + time.sleep(wait_time) + else: + raise + return wrapper + return decorator + + +# Usage +@rate_limit_retry(max_retries=3, base_delay=2) +def send_message(client, message: str): + response = client.create_chat_completion( + messages=[{"role": "user", "content": message}], + stream=False + ) + response.raise_for_status() + return response.json() +``` + +## Monitoring Rate Limits + +```python +import time +from collections import defaultdict + +class RequestMonitor: + """Monitor API request rates.""" + + def __init__(self, window_seconds=60): + self.window = window_seconds + self.requests = defaultdict(list) + self.lock = Lock() + + def record_request(self, endpoint: str): + """Record an API request.""" + with self.lock: + now = time.time() + self.requests[endpoint].append(now) + + # Remove old requests outside the window + cutoff = now - self.window + self.requests[endpoint] = [ + t for t in self.requests[endpoint] if t > cutoff + ] + + def get_rate(self, endpoint: str) -> float: + """Get requests per second for an endpoint.""" + with self.lock: + recent = self.requests[endpoint] + if not recent: + return 0.0 + return len(recent) / self.window + + def is_rate_limited(self, endpoint: str, limit: int) -> bool: + """Check if endpoint is rate limited.""" + with self.lock: + cutoff = time.time() - self.window + recent = [t for t in self.requests[endpoint] if t > cutoff] + return len(recent) >= limit + + +# Usage +monitor = RequestMonitor(window_seconds=60) + +def make_request(client, messages): + endpoint = "/api/v1/chat/completions" + + # Check if we're rate limited + if monitor.is_rate_limited(endpoint, limit=100): + print("Approaching rate limit, slowing down...") + time.sleep(1) + + monitor.record_request(endpoint) + response = client.create_chat_completion(messages=messages) + return response +``` + +## Best Practices + +1. **Implement backoff** - Use exponential backoff for retries +2. **Respect Retry-After** - Use the `retry_after` header when available +3. **Monitor usage** - Track request rates to avoid hitting limits +4. **Queue requests** - For batch operations, use rate limiting +5. **Handle gracefully** - Show user-friendly messages when rate limited +6. **Use async** - Better resource utilization with concurrent requests + +## See Also + +- [Error Handling](error_handling.md) - Comprehensive error handling guide +- [Exceptions Reference](../api/exceptions.md) - Exception types and attributes diff --git a/docs/advanced/streaming_events.md b/docs/advanced/streaming_events.md new file mode 100644 index 0000000..2368a8d --- /dev/null +++ b/docs/advanced/streaming_events.md @@ -0,0 +1,284 @@ +# Streaming Events + +FastGPT uses Server-Sent Events (SSE) for streaming responses. This guide covers all event types you may encounter. + +## SSE Format + +FastGPT sends events in this format: + +``` +event: eventType +data: {"key": "value"} + +event: anotherEvent +data: {"key": "value"} +``` + +## Event Types + +### 1. `data` Event + +The main streaming event, compatible with OpenAI's format: + +```python +import json + +for line in response.iter_lines(): + if line.startswith("data:"): + data = line[5:].strip() + if data and data != "[DONE]": + chunk = json.loads(data) + # Process OpenAI-compatible response + if "choices" in chunk and chunk["choices"]: + delta = chunk["choices"][0].get("delta", {}) + content = delta.get("content", "") + if content: + print(content, end="", flush=True) +``` + +### 2. `answer` Event + +Main chat response content (alternative format): + +```python +for line in response.iter_lines(): + if line.startswith("event:answer"): + # Next line contains the data + answer_data = json.loads(next_line[5:]) + print(answer_data.get("text", ""), end="", flush=True) +``` + +### 3. `fastAnswer` Event + +Quick reply content: + +```python +for line in response.iter_lines(): + if line.startswith("event:fastAnswer"): + fast_answer_data = json.loads(next_line[5:]) + print(f"Quick reply: {fast_answer_data}") +``` + +### 4. `flowNodeStatus` Event + +Workflow node status updates: + +```python +for line in response.iter_lines(): + if line.startswith("event:flowNodeStatus"): + status_data = json.loads(next_line[5:]) + status = status_data.get("status") # "running", "completed", "error" + node_name = status_data.get("nodeName") + print(f"[{status.upper()}] {node_name}") +``` + +### 5. `flowResponses` Event + +Complete node response data (requires `detail=True`): + +```python +client.create_chat_completion( + messages=[{"role": "user", "content": "Hello"}], + stream=True, + detail=True # Enable detailed responses +) + +# Then in the stream: +for line in response.iter_lines(): + if line.startswith("event:flowResponses"): + response_data = json.loads(next_line[5:]) + # Contains module execution details + module_name = response_data.get("moduleName") + tokens = response_data.get("tokens") + print(f"Module: {module_name}, Tokens: {tokens}") +``` + +### 6. `interactive` Event + +Interactive node (requires user input or selection): + +```python +for line in response.iter_lines(): + if line.startswith("event:interactive"): + interactive_data = json.loads(next_line[5:]) + interactive_type = interactive_data.get("type") + + if interactive_type == "userSelect": + options = interactive_data["params"]["userSelectOptions"] + print("Please select an option:") + for i, option in enumerate(options): + print(f"{i + 1}. {option['value']}") + + elif interactive_type == "userInput": + form_fields = interactive_data["params"]["inputForm"] + print("Please provide the following information:") + for field in form_fields: + print(f"- {field['label']}") +``` + +### 7. `updateVariables` Event + +Variable updates during execution: + +```python +for line in response.iter_lines(): + if line.startswith("event:updateVariables"): + var_data = json.loads(next_line[5:]) + variables = var_data.get("variables", {}) + print(f"Variables updated: {variables}") +``` + +### 8. `error` Event + +Error events: + +```python +for line in response.iter_lines(): + if line.startswith("event:error"): + error_data = json.loads(next_line[5:]) + error_message = error_data.get("message", "Unknown error") + error_type = error_data.get("type", "Error") + print(f"Error [{error_type}]: {error_message}") +``` + +### 9. `toolCall`, `toolParams`, `toolResponse` Events + +Tool/agent operation events: + +```python +for line in response.iter_lines(): + if line.startswith("event:toolCall"): + tool_data = json.loads(next_line[5:]) + tool_name = tool_data.get("toolName") + print(f"Tool called: {tool_name}") + + elif line.startswith("event:toolParams"): + params_data = json.loads(next_line[5:]) + print(f"Tool parameters: {params_data}") + + elif line.startswith("event:toolResponse"): + response_data = json.loads(next_line[5:]) + print(f"Tool response: {response_data}") +``` + +## Complete Event Handler + +```python +import json +from fastgpt_client import ChatClient + +def handle_all_events(response): + """Handle all streaming event types.""" + + buffer = "" + current_event = None + + for line in response.iter_lines(): + if not line: + continue + + # Event type line + if line.startswith("event:"): + current_event = line[6:].strip() + + # Data line + elif line.startswith("data:"): + data = line[5:].strip() + if not data or data == "[DONE]": + continue + + try: + data_obj = json.loads(data) + except json.JSONDecodeError: + continue + + # Handle based on event type + if current_event is None: + # Default: OpenAI-compatible format + if "choices" in data_obj and data_obj["choices"]: + delta = data_obj["choices"][0].get("delta", {}) + content = delta.get("content", "") + if content: + buffer += content + print(content, end="", flush=True) + + elif current_event == "answer": + text = data_obj.get("text", "") + if text: + buffer += text + print(text, end="", flush=True) + + elif current_event == "flowNodeStatus": + status = data_obj.get("status") + node = data_obj.get("nodeName", "Unknown") + print(f"\n[{status.upper()}] {node}") + + elif current_event == "interactive": + interactive_type = data_obj.get("type") + print(f"\n[INTERACTIVE] {interactive_type}") + print(f"Details: {data_obj.get('params', {})}") + + elif current_event == "error": + print(f"\n[ERROR] {data_obj.get('message', 'Unknown error')}") + + elif current_event == "toolCall": + print(f"\n[TOOL] Calling: {data_obj.get('toolName')}") + + # Reset event + current_event = None + + return buffer + + +# Usage +with ChatClient(api_key="fastgpt-xxxxx") as client: + response = client.create_chat_completion( + messages=[{"role": "user", "content": "Hello"}], + stream=True, + detail=True # Enable flow responses + ) + + full_response = handle_all_events(response) + print(f"\n\nFull response: {full_response}") +``` + +## Event Flow Example + +A typical streaming conversation might generate events like: + +``` +event:flowNodeStatus +data:{"status": "running", "nodeName": "Chat Node"} + +event:answer +data:{"text": "Hello"} + +event:answer +data:{"text": "! How"} + +event:answer +data:{"text": " can I help"} + +event:flowNodeStatus +data:{"status": "completed", "nodeName": "Chat Node"} + +event:flowResponses +data:{"moduleName": "Chat Node", "tokens": 50} + +data:{"choices": [{"delta": {"content": "Hello! How can I help"}}], "usage": {"total_tokens": 50}} + +data:[DONE] +``` + +## Best Practices + +1. **Handle `[DONE]`** - Check for end of stream +2. **Validate JSON** - Use try/except for parsing +3. **Buffer content** - Accumulate text for display +4. **Handle errors** - Watch for error events +5. **Check event types** - Use `startswith("event:")` to detect events + +## See Also + +- [Streaming Example](../examples/streaming.md) - Basic streaming usage +- [Detail Mode](detail_mode.md) - Enable detailed execution data diff --git a/docs/api/app_client.md b/docs/api/app_client.md new file mode 100644 index 0000000..ade1e77 --- /dev/null +++ b/docs/api/app_client.md @@ -0,0 +1,93 @@ +# AppClient + +The `AppClient` provides methods for application analytics and logs. + +## Initialization + +```python +from fastgpt_client import AppClient + +client = AppClient( + api_key="fastgpt-xxxxx", + base_url="http://localhost:3000" +) +``` + +## Methods + +### get_app_logs_chart + +Get application analytics chart data. + +```python +client.get_app_logs_chart( + appId: str, + dateStart: str, + dateEnd: str, + offset: int = 1, + source: list[str] | None = None, + userTimespan: str = "day", + chatTimespan: str = "day", + appTimespan: str = "day" +) -> httpx.Response +``` + +**Parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `appId` | `str` | Yes | Application ID | +| `dateStart` | `str` | Yes | Start date (ISO 8601 format: `YYYY-MM-DD`) | +| `dateEnd` | `str` | Yes | End date (ISO 8601 format: `YYYY-MM-DD`) | +| `offset` | `int` | No | Offset value (default: `1`) | +| `source` | `list[str]` | No | List of sources (default: `["api"]`) | +| `userTimespan` | `str` | No | User data timespan: `day`, `week`, or `month` (default: `"day"`) | +| `chatTimespan` | `str` | No | Chat data timespan: `day`, `week`, or `month` (default: `"day"`) | +| `appTimespan` | `str` | No | App data timespan: `day`, `week`, or `month` (default: `"day"`) | + +**Source Options:** + +- `"api"` - API interactions +- `"online"` - Online usage +- `"share"` - Shared links +- `"test"` - Test interactions + +**Example:** + +```python +from datetime import datetime, timedelta + +with AppClient(api_key="fastgpt-xxxxx") as client: + # Get analytics for the last 7 days + end_date = datetime.now() + start_date = end_date - timedelta(days=7) + + response = client.get_app_logs_chart( + appId="your-app-id", + dateStart=start_date.strftime("%Y-%m-%d"), + dateEnd=end_date.strftime("%Y-%m-%d"), + source=["api", "online"], + userTimespan="day", + chatTimespan="day", + appTimespan="day" + ) + + data = response.json() + + # Access analytics data + print(f"Users: {data['data'].get('users', {})}") + print(f"Chats: {data['data'].get('chats', {})}") + print(f"App metrics: {data['data'].get('app', {})}") +``` + +**Response Structure:** + +The response contains analytics data with the following possible keys: + +- `users` - User engagement metrics +- `chats` - Chat interaction metrics +- `app` - Application-level metrics +- `tokens` - Token usage statistics +- `prices` - Cost information + +Each metric may be organized by the specified timespan (day/week/month). diff --git a/docs/api/async_clients.md b/docs/api/async_clients.md new file mode 100644 index 0000000..0090767 --- /dev/null +++ b/docs/api/async_clients.md @@ -0,0 +1,201 @@ +# Async Clients + +The SDK provides full async/await support for high-performance applications. All synchronous clients have async equivalents. + +## Async ChatClient + +### Initialization + +```python +from fastgpt_client import AsyncChatClient + +client = AsyncChatClient( + api_key="fastgpt-xxxxx", + base_url="http://localhost:3000" +) +``` + +### Basic Usage + +```python +import asyncio +from fastgpt_client import AsyncChatClient + +async def main(): + async with AsyncChatClient(api_key="fastgpt-xxxxx") as client: + response = await client.create_chat_completion( + messages=[{"role": "user", "content": "Hello!"}], + stream=False + ) + response.raise_for_status() + result = response.json() + print(result['choices'][0]['message']['content']) + +asyncio.run(main()) +``` + +### Streaming with Async + +```python +import asyncio +import json +from fastgpt_client import AsyncChatClient + +async def stream_chat(): + async with AsyncChatClient(api_key="fastgpt-xxxxx") as client: + response = await client.create_chat_completion( + messages=[{"role": "user", "content": "Tell me a story"}], + stream=True + ) + + async for line in response.aiter_lines(): + if line.startswith("data:"): + data = line[5:].strip() + if data and data != "[DONE]": + chunk = json.loads(data) + if "choices" in chunk and chunk["choices"]: + delta = chunk["choices"][0].get("delta", {}) + content = delta.get("content", "") + if content: + print(content, end="", flush=True) + +asyncio.run(stream_chat()) +``` + +### Multiple Concurrent Requests + +One of the main benefits of async is handling multiple requests concurrently: + +```python +import asyncio +from fastgpt_client import AsyncChatClient + +async def fetch_multiple(): + async with AsyncChatClient(api_key="fastgpt-xxxxx") as client: + # Create multiple chat completions concurrently + tasks = [ + client.create_chat_completion( + messages=[{"role": "user", "content": f"What is {concept}?"}], + stream=False + ) + for concept in ["AI", "Machine Learning", "Deep Learning"] + ] + + responses = await asyncio.gather(*tasks) + + for i, response in enumerate(responses): + response.raise_for_status() + result = response.json() + concept = ["AI", "Machine Learning", "Deep Learning"][i] + print(f"\n{concept}:") + print(result['choices'][0]['message']['content']) + +asyncio.run(fetch_multiple()) +``` + +## Async AppClient + +### Basic Usage + +```python +import asyncio +from fastgpt_client import AsyncAppClient + +async def get_analytics(): + async with AsyncAppClient(api_key="fastgpt-xxxxx") as client: + response = await client.get_app_logs_chart( + appId="your-app-id", + dateStart="2024-01-01", + dateEnd="2024-12-31", + source=["api"] + ) + response.raise_for_status() + data = response.json() + print(data) + +asyncio.run(get_analytics()) +``` + +## Complete Example: Async Chat Application + +```python +import asyncio +from fastgpt_client import AsyncChatClient + +class AsyncChatApp: + def __init__(self, api_key: str, base_url: str): + self.client = AsyncChatClient(api_key=api_key, base_url=base_url) + self.chat_id = None + + async def start(self): + await self.client.__aenter__() + + async def stop(self): + await self.client.__aexit__(None, None, None) + + async def send_message(self, content: str) -> str: + response = await self.client.create_chat_completion( + messages=[{"role": "user", "content": content}], + chatId=self.chat_id, + stream=False + ) + response.raise_for_status() + result = response.json() + + # Update chat_id after first message + if not self.chat_id: + self.chat_id = result.get('chatId') + + return result['choices'][0]['message']['content'] + + async def chat(self): + await self.start() + try: + while True: + user_input = input("\nYou: ") + if user_input.lower() in ['quit', 'exit']: + break + + print("AI: ", end="", flush=True) + response = await self.send_message(user_input) + print(response) + finally: + await self.stop() + +async def main(): + app = AsyncChatApp( + api_key="fastgpt-xxxxx", + base_url="http://localhost:3000" + ) + await app.chat() + +asyncio.run(main()) +``` + +## Key Differences from Sync Clients + +| Aspect | Sync | Async | +|--------|------|-------| +| Context Manager | `with` | `async with` | +| Method Call | `client.method()` | `await client.method()` | +| Streaming | `for line in response.iter_lines()` | `async for line in response.aiter_lines()` | +| Close | `client.close()` | `await client.close()` | + +## Best Practices + +1. **Always use `async with`** for automatic resource cleanup +2. **Use `asyncio.gather()`** for concurrent requests +3. **Handle exceptions properly** with try/except blocks +4. **Close clients** when done (or use context managers) +5. **Avoid mixing sync and async** code in the same application + +## When to Use Async + +Use async clients when you need to: + +- Handle many concurrent requests +- Integrate with other async libraries (FastAPI, aiohttp, etc.) +- Build real-time applications with streaming +- Maximize throughput in I/O-bound applications + +For simple scripts or applications with low concurrency, sync clients are often simpler and equally effective. diff --git a/docs/api/chat_client.md b/docs/api/chat_client.md new file mode 100644 index 0000000..9c381d2 --- /dev/null +++ b/docs/api/chat_client.md @@ -0,0 +1,315 @@ +# ChatClient + +The `ChatClient` provides methods for chat completions and conversation management. + +## Initialization + +```python +from fastgpt_client import ChatClient + +client = ChatClient( + api_key="fastgpt-xxxxx", + base_url="http://localhost:3000" +) +``` + +## Methods + +### create_chat_completion + +Create a chat completion (synchronous or streaming). + +```python +client.create_chat_completion( + messages: list[dict], + stream: bool = False, + chatId: str | None = None, + detail: bool = False, + variables: dict[str, Any] | None = None, + responseChatItemId: str | None = None +) -> httpx.Response +``` + +**Parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `messages` | `list[dict]` | Yes | Array of message objects with `role` and `content` | +| `stream` | `bool` | No | Whether to stream the response (default: `False`) | +| `chatId` | `str` | No | Chat ID for conversation context | +| `detail` | `bool` | No | Whether to return detailed execution data (default: `False`) | +| `variables` | `dict` | No | Template variables for substitution | +| `responseChatItemId` | `str` | No | Custom ID for the response message | + +**Example:** + +```python +response = client.create_chat_completion( + messages=[ + {"role": "user", "content": "Hello!"}, + {"role": "assistant", "content": "Hi there!"}, + {"role": "user", "content": "How are you?"} + ], + stream=False +) +result = response.json() +print(result['choices'][0]['message']['content']) +``` + +--- + +### get_chat_histories + +Get chat histories for an application. + +```python +client.get_chat_histories( + appId: str, + offset: int = 0, + pageSize: int = 20, + source: Literal["api", "online", "share", "test"] = "api" +) -> httpx.Response +``` + +**Parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `appId` | `str` | Yes | Application ID | +| `offset` | `int` | No | Offset for pagination (default: `0`) | +| `pageSize` | `int` | No | Number of records per page (default: `20`) | +| `source` | `str` | No | Source filter (default: `"api"`) | + +**Example:** + +```python +response = client.get_chat_histories( + appId="app-123", + offset=0, + pageSize=20, + source="api" +) +data = response.json() +for chat in data['data']['list']: + print(f"{chat['title']}: {chat['chatId']}") +``` + +--- + +### get_chat_init + +Get chat initialization information. + +```python +client.get_chat_init( + appId: str, + chatId: str +) -> httpx.Response +``` + +**Parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `appId` | `str` | Yes | Application ID | +| `chatId` | `str` | Yes | Chat ID | + +--- + +### get_chat_records + +Get chat records for a specific chat. + +```python +client.get_chat_records( + appId: str, + chatId: str, + offset: int = 0, + pageSize: int = 10, + loadCustomFeedbacks: bool = False +) -> httpx.Response +``` + +**Parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `appId` | `str` | Yes | Application ID | +| `chatId` | `str` | Yes | Chat ID | +| `offset` | `int` | No | Offset for pagination (default: `0`) | +| `pageSize` | `int` | No | Number of records per page (default: `10`) | +| `loadCustomFeedbacks` | `bool` | No | Whether to load custom feedbacks (default: `False`) | + +--- + +### get_record_detail + +Get detailed execution data for a specific record. + +```python +client.get_record_detail( + appId: str, + chatId: str, + dataId: str +) -> httpx.Response +``` + +**Parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `appId` | `str` | Yes | Application ID | +| `chatId` | `str` | Yes | Chat ID | +| `dataId` | `str` | Yes | Record ID | + +--- + +### update_chat_history + +Update chat history (title or pin status). + +```python +client.update_chat_history( + appId: str, + chatId: str, + customTitle: str | None = None, + top: bool | None = None +) -> httpx.Response +``` + +**Parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `appId` | `str` | Yes | Application ID | +| `chatId` | `str` | Yes | Chat ID | +| `customTitle` | `str` | No | Custom title for the chat | +| `top` | `bool` | No | Whether to pin the chat | + +--- + +### delete_chat_history + +Delete a chat history. + +```python +client.delete_chat_history( + appId: str, + chatId: str +) -> httpx.Response +``` + +**Parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `appId` | `str` | Yes | Application ID | +| `chatId` | `str` | Yes | Chat ID | + +--- + +### clear_chat_histories + +Clear all chat histories for an application. + +```python +client.clear_chat_histories( + appId: str +) -> httpx.Response +``` + +**Parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `appId` | `str` | Yes | Application ID | + +--- + +### delete_chat_record + +Delete a single chat record. + +```python +client.delete_chat_record( + appId: str, + chatId: str, + contentId: str +) -> httpx.Response +``` + +**Parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `appId` | `str` | Yes | Application ID | +| `chatId` | `str` | Yes | Chat ID | +| `contentId` | `str` | Yes | Content ID of the record | + +--- + +### send_feedback + +Send feedback for a chat message (like/dislike). + +```python +client.send_feedback( + appId: str, + chatId: str, + dataId: str, + userGoodFeedback: str | None = None, + userBadFeedback: str | None = None +) -> httpx.Response +``` + +**Parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `appId` | `str` | Yes | Application ID | +| `chatId` | `str` | Yes | Chat ID | +| `dataId` | `str` | Yes | Message ID | +| `userGoodFeedback` | `str` | No | Positive feedback text | +| `userBadFeedback` | `str` | No | Negative feedback text | + +**Example:** + +```python +# Like a message +client.send_feedback( + appId="app-123", + chatId="chat-123", + dataId="msg-123", + userGoodFeedback="Great answer!" +) + +# Dislike a message +client.send_feedback( + appId="app-123", + chatId="chat-123", + dataId="msg-123", + userBadFeedback="Not helpful" +) +``` + +--- + +### get_suggested_questions + +Get suggested questions based on chat context. + +```python +client.get_suggested_questions( + appId: str, + chatId: str, + questionGuide: dict[str, Any] | None = None +) -> httpx.Response +``` + +**Parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `appId` | `str` | Yes | Application ID | +| `chatId` | `str` | Yes | Chat ID | +| `questionGuide` | `dict` | No | Custom configuration for question guide | diff --git a/docs/api/exceptions.md b/docs/api/exceptions.md new file mode 100644 index 0000000..c8015ef --- /dev/null +++ b/docs/api/exceptions.md @@ -0,0 +1,248 @@ +# Exceptions + +The SDK provides specific exceptions for different error types. All exceptions inherit from the base `FastGPTError`. + +## Exception Hierarchy + +``` +FastGPTError +├── APIError +├── AuthenticationError +├── RateLimitError +├── ValidationError +└── StreamParseError +``` + +## Exception Types + +### FastGPTError + +Base exception class for all FastGPT SDK errors. + +```python +from fastgpt_client.exceptions import FastGPTError + +try: + # SDK operation + pass +except FastGPTError as e: + print(f"FastGPT error: {e}") +``` + +--- + +### APIError + +General API error for 4xx and 5xx responses that don't have a specific exception type. + +```python +from fastgpt_client.exceptions import APIError + +try: + response = client.create_chat_completion(...) +except APIError as e: + print(f"API error: {e.message}") + print(f"Status code: {e.status_code}") + print(f"Response data: {e.response_data}") +``` + +**Attributes:** + +| Attribute | Type | Description | +|-----------|------|-------------| +| `message` | `str` | Error message | +| `status_code` | `int` | HTTP status code | +| `response_data` | `dict | None` | Full error response data | + +--- + +### AuthenticationError + +Raised when authentication fails (401 status code). + +```python +from fastgpt_client.exceptions import AuthenticationError + +try: + response = client.create_chat_completion(...) +except AuthenticationError as e: + print(f"Authentication failed: {e.message}") + print("Please check your API key") +``` + +**Common Causes:** + +- Invalid API key +- Missing API key +- Expired API key +- API key format incorrect + +--- + +### RateLimitError + +Raised when rate limit is exceeded (429 status code). + +```python +from fastgpt_client.exceptions import RateLimitError + +try: + response = client.create_chat_completion(...) +except RateLimitError as e: + print(f"Rate limit exceeded: {e.message}") + if e.retry_after: + print(f"Retry after {e.retry_after} seconds") +``` + +**Attributes:** + +| Attribute | Type | Description | +|-----------|------|-------------| +| `message` | `str` | Error message | +| `retry_after` | `str | None` | Suggested retry delay (from `Retry-After` header) | +| `status_code` | `int` | HTTP status code (429) | +| `response_data` | `dict | None` | Full error response data | + +--- + +### ValidationError + +Raised when request validation fails (422 status code). + +```python +from fastgpt_client.exceptions import ValidationError + +try: + response = client.create_chat_completion( + messages=[{"role": "user", "content": "Hello"}], + invalid_param="value" + ) +except ValidationError as e: + print(f"Validation error: {e.message}") + print(f"Response data: {e.response_data}") +``` + +**Common Causes:** + +- Invalid parameter values +- Missing required parameters +- Incorrect parameter types +- Invalid message format + +--- + +### StreamParseError + +Raised when parsing streaming responses fails. + +```python +from fastgpt_client.exceptions import StreamParseError + +try: + for line in response.iter_lines(): + # Parse streaming data + pass +except StreamParseError as e: + print(f"Stream parsing error: {e.message}") +``` + +--- + +## Comprehensive Error Handling + +```python +from fastgpt_client import ChatClient +from fastgpt_client.exceptions import ( + APIError, + AuthenticationError, + RateLimitError, + ValidationError, + FastGPTError +) + +def safe_chat_completion(client, messages): + """Handle chat completion with comprehensive error handling.""" + try: + response = client.create_chat_completion( + messages=messages, + stream=False + ) + response.raise_for_status() + return response.json() + + except AuthenticationError: + print("Error: Invalid API key. Please check your credentials.") + return None + + except RateLimitError as e: + print(f"Error: Rate limit exceeded. Please wait.") + if e.retry_after: + print(f"Retry after {e.retry_after} seconds") + return None + + except ValidationError as e: + print(f"Error: Invalid request parameters: {e.message}") + return None + + except APIError as e: + print(f"Error: API request failed: {e.message}") + return None + + except FastGPTError as e: + print(f"Error: Unexpected FastGPT error: {e}") + return None + + except Exception as e: + print(f"Error: Unexpected error: {e}") + return None + +# Usage +with ChatClient(api_key="fastgpt-xxxxx") as client: + result = safe_chat_completion( + client, + [{"role": "user", "content": "Hello!"}] + ) +``` + +## Error Handling Best Practices + +1. **Always handle exceptions** when making API calls +2. **Use specific exceptions** for better error handling +3. **Log errors** for debugging purposes +4. **Provide user-friendly messages** based on error type +5. **Implement retry logic** for rate limit errors +6. **Validate parameters** before making API calls + +## Retry Logic for Rate Limiting + +```python +import time +from fastgpt_client import ChatClient +from fastgpt_client.exceptions import RateLimitError + +def chat_with_retry(client, messages, max_retries=3): + """Retry chat completion on rate limit errors.""" + for attempt in range(max_retries): + try: + response = client.create_chat_completion( + messages=messages, + stream=False + ) + response.raise_for_status() + return response.json() + + except RateLimitError as e: + if attempt < max_retries - 1: + # Use Retry-After header or default delay + delay = int(e.retry_after) if e.retry_after else 5 + print(f"Rate limited. Waiting {delay} seconds...") + time.sleep(delay) + else: + print(f"Max retries exceeded. Giving up.") + raise +``` + +## Next Steps + +- [Error Handling Guide](../advanced/error_handling.md) - More error handling strategies +- [Rate Limiting](../advanced/rate_limiting.md) - Handling rate limits effectively diff --git a/docs/api/overview.md b/docs/api/overview.md new file mode 100644 index 0000000..098cfe6 --- /dev/null +++ b/docs/api/overview.md @@ -0,0 +1,104 @@ +# API Reference Overview + +The FastGPT Python SDK provides three main client types: + +## Clients + +| Client | Sync | Async | Description | +|--------|------|-------|-------------| +| [`ChatClient`](chat_client.md) | ✅ | ✅ | Chat completions and conversation management | +| [`AppClient`](app_client.md) | ✅ | ✅ | App analytics and logs | +| `FastGPTClient` | ✅ | ✅ | Base client (usually used indirectly) | + +## Base Client Options + +All clients share these initialization parameters: + +```python +from fastgpt_client import ChatClient + +client = ChatClient( + api_key="fastgpt-xxxxx", # Required: Your API key + base_url="http://localhost:3000", # Optional: API base URL + timeout=60.0, # Optional: Request timeout (seconds) + max_retries=3, # Optional: Max retry attempts + retry_delay=1.0, # Optional: Delay between retries (seconds) + enable_logging=False # Optional: Enable request logging +) +``` + +### Parameters + +- **api_key** (`str`): Your FastGPT API key +- **base_url** (`str`): Base URL for the FastGPT API (default: `"http://localhost:3000"`) +- **timeout** (`float`): Request timeout in seconds (default: `60.0`) +- **max_retries** (`int`): Maximum number of retry attempts (default: `3`) +- **retry_delay** (`float`): Delay between retries in seconds (default: `1.0`) +- **enable_logging** (`bool`): Whether to enable request logging (default: `False`) + +## Context Manager Support + +All clients support context managers for automatic resource cleanup: + +```python +# Synchronous +with ChatClient(api_key="fastgpt-xxxxx") as client: + response = client.create_chat_completion(...) +# Client is automatically closed + +# Asynchronous +async with AsyncChatClient(api_key="fastgpt-xxxxx") as client: + response = await client.create_chat_completion(...) +# Client is automatically closed +``` + +## Response Format + +All API methods return `httpx.Response` objects. You can: + +```python +# Raise exception for 4xx/5xx responses +response.raise_for_status() + +# Get JSON data +data = response.json() + +# Get status code +status = response.status_code + +# Get headers +headers = response.headers +``` + +## Async Variants + +All synchronous clients have async equivalents: + +```python +# Sync +from fastgpt_client import ChatClient + +with ChatClient(api_key="...") as client: + response = client.create_chat_completion(...) + +# Async +from fastgpt_client import AsyncChatClient + +async with AsyncChatClient(api_key="...") as client: + response = await client.create_chat_completion(...) +``` + +See [Async Clients](async_clients.md) for more details. + +## Exceptions + +The SDK provides specific exceptions for different error types: + +| Exception | Status Code | Description | +|-----------|-------------|-------------| +| `AuthenticationError` | 401 | Invalid or missing API key | +| `RateLimitError` | 429 | Too many requests | +| `ValidationError` | 422 | Invalid request parameters | +| `APIError` | 4xx/5xx | General API errors | + +See [Exceptions](exceptions.md) for more details. diff --git a/docs/development.md b/docs/development.md new file mode 100644 index 0000000..5672997 --- /dev/null +++ b/docs/development.md @@ -0,0 +1,223 @@ +# Development Guide + +This guide covers contributing to and developing the FastGPT Python SDK. + +## Development Setup + +### Clone the Repository + +```bash +git clone https://github.com/yourusername/fastgpt-python-sdk.git +cd fastgpt-python-sdk +``` + +### Install in Development Mode + +```bash +pip install -e ".[dev]" +``` + +This installs: +- The SDK in editable mode +- Development dependencies (pytest, ruff, etc.) + +## Project Structure + +``` +fastgpt-python-sdk/ +├── fastgpt_client/ +│ ├── __init__.py # Public API exports +│ ├── client.py # Sync clients (ChatClient, AppClient) +│ ├── async_client.py # Async clients +│ ├── base_client.py # Base functionality (retry, validation) +│ └── exceptions.py # Custom exceptions +├── tests/ +│ ├── test_chat_client.py # ChatClient tests +│ ├── test_app_client.py # AppClient tests +│ └── test_async_client.py # Async client tests +├── examples/ +│ ├── basic_usage.py # Basic usage examples +│ └── async_usage.py # Async usage examples +├── docs/ # MkDocs documentation +├── setup.py # Package setup +├── pyproject.toml # Project configuration +└── mkdocs.yml # Documentation config +``` + +## Running Tests + +### Run All Tests + +```bash +pytest +``` + +### Run Specific Test File + +```bash +pytest tests/test_chat_client.py +``` + +### Run with Coverage + +```bash +pytest --cov=fastgpt_client --cov-report=html +``` + +### Run with Verbose Output + +```bash +pytest -v +``` + +## Code Quality + +### Lint with Ruff + +```bash +ruff check fastgpt_client/ +``` + +### Format with Ruff + +```bash +ruff format fastgpt_client/ +``` + +### Fix Linting Issues + +```bash +ruff check --fix fastgpt_client/ +``` + +## Building Documentation + +### Install Documentation Dependencies + +```bash +pip install mkdocs-material mkdocstrings[python] +``` + +### Build Documentation + +```bash +mkdocs build +``` + +### Serve Documentation Locally + +```bash +mkdocs serve +``` + +Then open http://127.0.0.1:8000 in your browser. + +## Creating a Release + +### Update Version + +Update `fastgpt_client/__init__.py`: + +```python +__version__ = "0.2.0" +``` + +### Build Package + +```bash +python -m build +``` + +### Publish to PyPI + +```bash +twine upload dist/* +``` + +## Contribution Guidelines + +### Code Style + +- Use **Google-style docstrings** +- Follow **PEP 8** formatting (handled by Ruff) +- Keep functions focused and single-purpose +- Add type hints for all public methods + +### Adding Features + +1. **Create a feature branch** + ```bash + git checkout -b feature/your-feature-name + ``` + +2. **Write tests first** + ```python + def test_new_feature(): + client = ChatClient(api_key="test-key") + result = client.new_method() + assert result is not None + ``` + +3. **Implement the feature** + ```python + def new_method(self): + """Do something new. + + Returns: + The result. + """ + # Implementation + pass + ``` + +4. **Add documentation** + - Update docstrings + - Add usage examples + - Update API reference + +5. **Run tests** + ```bash + pytest + ``` + +6. **Submit a pull request** + +### Writing Tests + +```python +import pytest +from fastgpt_client import ChatClient +from httpx import Response + +def test_create_chat_completion(): + """Test chat completion creation.""" + client = ChatClient(api_key="test-key") + + response = client.create_chat_completion( + messages=[{"role": "user", "content": "Hello"}], + stream=False + ) + + assert response.status_code == 200 +``` + +## Useful Commands + +| Command | Description | +|---------|-------------| +| `pytest` | Run all tests | +| `pytest -v` | Verbose test output | +| `pytest --cov` | Run with coverage | +| `ruff check` | Lint code | +| `ruff format` | Format code | +| `mkdocs serve` | Serve docs locally | +| `mkdocs build` | Build docs | +| `python -m build` | Build package | + +## Resources + +- [FastGPT Documentation](https://doc.fastgpt.io/) +- [Chat API Docs](https://doc.fastgpt.io/docs/introduction/development/openapi/chat) +- [PyPI Publishing Guide](https://packaging.python.org/tutorials/packaging-projects/) +- [pytest Documentation](https://docs.pytest.org/) +- [Ruff Documentation](https://docs.astral.sh/ruff/) diff --git a/docs/examples/async_usage.md b/docs/examples/async_usage.md new file mode 100644 index 0000000..b16adbd --- /dev/null +++ b/docs/examples/async_usage.md @@ -0,0 +1,340 @@ +# Async Usage Example + +Complete examples demonstrating asynchronous usage of the FastGPT Python SDK. + +## Why Use Async? + +Async is beneficial when you need to: + +- Handle multiple concurrent requests efficiently +- Integrate with async frameworks (FastAPI, aiohttp) +- Build real-time streaming applications +- Maximize throughput in I/O-bound applications + +## Installation + +```bash +pip install fastgpt-client +``` + +## Basic Async Chat + +```python +import asyncio +from fastgpt_client import AsyncChatClient + +async def simple_chat(): + """Simple async chat completion.""" + async with AsyncChatClient(api_key="fastgpt-xxxxx") as client: + response = await client.create_chat_completion( + messages=[{"role": "user", "content": "Hello! What's AI?"}], + stream=False + ) + response.raise_for_status() + result = response.json() + print(result['choices'][0]['message']['content']) + +asyncio.run(simple_chat()) +``` + +## Async Streaming + +```python +import asyncio +import json +from fastgpt_client import AsyncChatClient + +async def stream_chat(): + """Async streaming chat completion.""" + async with AsyncChatClient(api_key="fastgpt-xxxxx") as client: + response = await client.create_chat_completion( + messages=[{"role": "user", "content": "Tell me a short story"}], + stream=True + ) + + async for line in response.aiter_lines(): + if line.startswith("data:"): + data = line[5:].strip() + if data and data != "[DONE]": + chunk = json.loads(data) + if "choices" in chunk and chunk["choices"]: + delta = chunk["choices"][0].get("delta", {}) + content = delta.get("content", "") + if content: + print(content, end="", flush=True) + print() + +asyncio.run(stream_chat()) +``` + +## Multiple Concurrent Requests + +```python +import asyncio +from fastgpt_client import AsyncChatClient + +async def fetch_multiple(): + """Run multiple requests concurrently.""" + async with AsyncChatClient(api_key="fastgpt-xxxxx") as client: + # Create multiple tasks + tasks = [ + client.create_chat_completion( + messages=[{"role": "user", "content": f"What is {concept}?"}], + stream=False + ) + for concept in ["AI", "Machine Learning", "Deep Learning"] + ] + + # Execute all tasks concurrently + responses = await asyncio.gather(*tasks) + + # Process results + for i, response in enumerate(responses): + response.raise_for_status() + result = response.json() + concept = ["AI", "Machine Learning", "Deep Learning"][i] + print(f"\n{concept}:") + print(f" {result['choices'][0]['message']['content'][:100]}...") + +asyncio.run(fetch_multiple()) +``` + +## Complete Async Example + +```python +"""Complete async usage example for FastGPT Python SDK.""" + +import asyncio +import json +import os +from dotenv import load_dotenv +from datetime import datetime, timedelta + +from fastgpt_client import AsyncChatClient, AsyncAppClient + +load_dotenv() + +API_KEY = os.getenv("API_KEY") +BASE_URL = os.getenv("BASE_URL") + + +async def simple_chat(): + """Simple async chat completion example.""" + async with AsyncChatClient(api_key=API_KEY, base_url=BASE_URL) as client: + response = await client.create_chat_completion( + messages=[{"role": "user", "content": "Hello! What's AI?"}], + stream=False + ) + response.raise_for_status() + result = response.json() + print("Response:", result['choices'][0]['message']['content']) + + +async def streaming_chat(): + """Async streaming chat completion example.""" + async with AsyncChatClient(api_key=API_KEY, base_url=BASE_URL) as client: + response = await client.create_chat_completion( + messages=[{"role": "user", "content": "Tell me a short story"}], + stream=True + ) + + print("Streaming response: ", end="", flush=True) + async for line in response.aiter_lines(): + if line.startswith("data:"): + data = line[5:].strip() + if data and data != "[DONE]": + chunk = json.loads(data) + if "choices" in chunk and chunk["choices"]: + delta = chunk["choices"][0].get("delta", {}) + content = delta.get("content", "") + if content: + print(content, end="", flush=True) + print() + + +async def chat_with_context(): + """Async chat with context using chatId.""" + async with AsyncChatClient(api_key=API_KEY, base_url=BASE_URL) as client: + chat_id = "my_async_chat_123" + + # First message + print("User: What's AI?") + response = await client.create_chat_completion( + messages=[{"role": "user", "content": "What's AI?"}], + chatId=chat_id, + stream=False + ) + response.raise_for_status() + result = response.json() + print(f"AI: {result['choices'][0]['message']['content']}\n") + + # Second message (continues the conversation) + print("User: Tell me more about it") + response = await client.create_chat_completion( + messages=[{"role": "user", "content": "Tell me more about it"}], + chatId=chat_id, + stream=False + ) + response.raise_for_status() + result = response.json() + print(f"AI: {result['choices'][0]['message']['content']}") + + +async def get_histories(): + """Async get chat histories.""" + async with AsyncChatClient(api_key=API_KEY, base_url=BASE_URL) as client: + app_id = os.getenv("APP_ID", "default-app-id") + + try: + histories = await client.get_chat_histories( + appId=app_id, + offset=0, + pageSize=20, + source="api" + ) + histories.raise_for_status() + data = histories.json() + print(f"Total chats: {data['data']['total']}") + except Exception as e: + print(f"Error: {e}") + + +async def get_app_analytics(): + """Async get app analytics.""" + async with AsyncAppClient(api_key=API_KEY, base_url=BASE_URL) as client: + app_id = os.getenv("APP_ID", "default-app-id") + + try: + # Get analytics for the last 7 days + end_date = datetime.now() + start_date = end_date - timedelta(days=7) + + logs = await client.get_app_logs_chart( + appId=app_id, + dateStart=start_date.strftime("%Y-%m-%d"), + dateEnd=end_date.strftime("%Y-%m-%d"), + source=["api"], + userTimespan="day", + chatTimespan="day", + appTimespan="day" + ) + logs.raise_for_status() + data = logs.json() + print("App Analytics (last 7 days):") + print(f" Data keys: {list(data['data'].keys())}") + except Exception as e: + print(f"Error: {e}") + + +async def multiple_requests(): + """Run multiple async requests concurrently.""" + async with AsyncChatClient(api_key=API_KEY, base_url=BASE_URL) as client: + # Create multiple chat completions concurrently + tasks = [ + client.create_chat_completion( + messages=[{"role": "user", "content": f"What is {concept}?"}], + stream=False + ) + for concept in ["AI", "Machine Learning", "Deep Learning"] + ] + + responses = await asyncio.gather(*tasks) + + for i, response in enumerate(responses): + response.raise_for_status() + result = response.json() + concept = ["AI", "Machine Learning", "Deep Learning"][i] + print(f"\n{concept}:") + print(f" {result['choices'][0]['message']['content'][:100]}...") + + +async def chat_with_variables(): + """Async chat with template variables.""" + async with AsyncChatClient(api_key=API_KEY, base_url=BASE_URL) as client: + response = await client.create_chat_completion( + messages=[{"role": "user", "content": "Introduction"}], + variables={ + "user_name": "Alice", + "company": "Tech Corp", + "language": "English" + }, + stream=False + ) + response.raise_for_status() + result = response.json() + print("Response with variables:", result['choices'][0]['message']['content']) + + +async def main(): + """Run all examples.""" + print("=== Simple Chat ===") + try: + await simple_chat() + except Exception as e: + print(f"Error: {e}") + + print("\n=== Streaming Chat ===") + try: + await streaming_chat() + except Exception as e: + print(f"Error: {e}") + + print("\n=== Chat with Context ===") + try: + await chat_with_context() + except Exception as e: + print(f"Error: {e}") + + print("\n=== Multiple Requests (Concurrent) ===") + try: + await multiple_requests() + except Exception as e: + print(f"Error: {e}") + + print("\n=== Chat with Variables ===") + try: + await chat_with_variables() + except Exception as e: + print(f"Error: {e}") + + +if __name__ == "__main__": + asyncio.run(main()) +``` + +## Async with FastAPI + +```python +from fastapi import FastAPI +from fastgpt_client import AsyncChatClient +import os + +app = FastAPI() + +# Initialize client at startup +@app.on_event("startup") +async def startup(): + app.state.client = AsyncChatClient( + api_key=os.getenv("API_KEY"), + base_url=os.getenv("BASE_URL") + ) + +@app.on_event("shutdown") +async def shutdown(): + await app.state.client.close() + +@app.post("/chat") +async def chat(message: str): + response = await app.state.client.create_chat_completion( + messages=[{"role": "user", "content": message}], + stream=False + ) + result = response.json() + return {"response": result['choices'][0]['message']['content']} +``` + +## See Also + +- [Async Clients](../api/async_clients.md) - Complete async API reference +- [Streaming](streaming.md) - Streaming examples +- [Basic Usage](basic_usage.md) - Synchronous examples diff --git a/docs/examples/basic_usage.md b/docs/examples/basic_usage.md new file mode 100644 index 0000000..4412128 --- /dev/null +++ b/docs/examples/basic_usage.md @@ -0,0 +1,204 @@ +# Basic Usage Example + +A complete example demonstrating basic usage of the FastGPT Python SDK. + +## Setup + +First, install the SDK and create a `.env` file: + +```bash +pip install fastgpt-client python-dotenv +``` + +```bash +# .env +API_KEY=fastgpt-xxxxx +BASE_URL=http://localhost:3000 +``` + +## Complete Example + +```python +"""Basic usage example for FastGPT Python SDK.""" + +from fastgpt_client import ChatClient +from dotenv import load_dotenv +import os + +load_dotenv() + +# Configure your API key and base URL +API_KEY = os.getenv("API_KEY") +BASE_URL = os.getenv("BASE_URL") + + +def simple_chat(): + """Simple chat completion example.""" + with ChatClient(api_key=API_KEY, base_url=BASE_URL) as client: + response = client.create_chat_completion( + messages=[{"role": "user", "content": "Hello! What's AI?"}], + stream=False + ) + response.raise_for_status() + result = response.json() + + print("Response:", result['choices'][0]['message']['content']) + + +def chat_with_history(): + """Chat with message history example.""" + with ChatClient(api_key=API_KEY, base_url=BASE_URL) as client: + response = client.create_chat_completion( + messages=[ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": "Hello!"}, + {"role": "assistant", "content": "Hi there! How can I help you?"}, + {"role": "user", "content": "What's the capital of France?"} + ], + stream=False + ) + response.raise_for_status() + result = response.json() + + print("Response:", result['choices'][0]['message']['content']) + + +def multi_turn_conversation(): + """Multi-turn conversation example.""" + with ChatClient(api_key=API_KEY, base_url=BASE_URL) as client: + conversation = [ + {"role": "user", "content": "What's Python?"} + ] + + # First turn + response = client.create_chat_completion( + messages=conversation, + stream=False + ) + response.raise_for_status() + result = response.json() + assistant_message = result['choices'][0]['message']['content'] + print(f"AI: {assistant_message}") + + # Add assistant response to conversation + conversation.append({"role": "assistant", "content": assistant_message}) + + # Second turn + conversation.append({"role": "user", "content": "Give me an example"}) + response = client.create_chat_completion( + messages=conversation, + stream=False + ) + response.raise_for_status() + result = response.json() + assistant_message = result['choices'][0]['message']['content'] + print(f"AI: {assistant_message}") + + +if __name__ == "__main__": + print("=== Simple Chat ===") + try: + simple_chat() + except Exception as e: + print(f"Error: {e}") + + print("\n=== Chat with History ===") + try: + chat_with_history() + except Exception as e: + print(f"Error: {e}") + + print("\n=== Multi-turn Conversation ===") + try: + multi_turn_conversation() + except Exception as e: + print(f"Error: {e}") +``` + +## Running the Example + +```bash +python basic_usage.py +``` + +## Expected Output + +``` +=== Simple Chat === +Response: AI (Artificial Intelligence) is a branch of computer science... + +=== Chat with History === +Response: The capital of France is Paris. + +=== Multi-turn Conversation === +AI: Python is a high-level programming language... +AI: Here's a simple example: print("Hello, World!") +``` + +## Key Concepts + +### Messages Array + +The `messages` parameter is an array of message objects: + +```python +messages = [ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": "Hello!"}, + {"role": "assistant", "content": "Hi there!"}, + {"role": "user", "content": "How are you?"} +] +``` + +### Response Structure + +The response follows OpenAI's format: + +```python +{ + "id": "chatcmpl-xxx", + "object": "chat.completion", + "created": 1234567890, + "model": "gpt-3.5-turbo", + "choices": [ + { + "index": 0, + "message": { + "role": "assistant", + "content": "Response text here..." + }, + "finish_reason": "stop" + } + ], + "usage": { + "prompt_tokens": 20, + "completion_tokens": 15, + "total_tokens": 35 + } +} +``` + +### Error Handling + +Always handle potential errors: + +```python +from fastgpt_client import ChatClient +from fastgpt_client.exceptions import APIError, AuthenticationError + +try: + with ChatClient(api_key=API_KEY) as client: + response = client.create_chat_completion(...) + response.raise_for_status() + result = response.json() +except AuthenticationError: + print("Invalid API key") +except APIError as e: + print(f"API error: {e}") +``` + +## See Also + +- [Streaming Example](streaming.md) - Learn how to use streaming responses +- [Async Usage](async_usage.md) - Asynchronous examples +- [Chat Context](chat_context.md) - Managing conversation context diff --git a/docs/examples/chat_context.md b/docs/examples/chat_context.md new file mode 100644 index 0000000..f08a231 --- /dev/null +++ b/docs/examples/chat_context.md @@ -0,0 +1,251 @@ +# Chat Context Example + +Learn how to maintain conversation context across multiple requests using `chatId`. + +## Understanding chatId + +The `chatId` parameter allows you to: + +- **Maintain conversation history** - The AI remembers previous messages +- **Resume conversations** - Continue an existing chat session +- **Organize conversations** - Keep separate conversations for different users/topics + +## Basic Context Usage + +```python +from fastgpt_client import ChatClient + +with ChatClient(api_key="fastgpt-xxxxx") as client: + chat_id = "conversation_123" + + # First message + response = client.create_chat_completion( + messages=[{"role": "user", "content": "My name is Alice"}], + chatId=chat_id, + stream=False + ) + result = response.json() + print(f"AI: {result['choices'][0]['message']['content']}") + + # Second message - AI remembers the name! + response = client.create_chat_completion( + messages=[{"role": "user", "content": "What's my name?"}], + chatId=chat_id, # Same chatId maintains context + stream=False + ) + result = response.json() + print(f"AI: {result['choices'][0]['message']['content']}") + # Output: "Your name is Alice!" +``` + +## Multi-Turn Conversation Example + +```python +"""Multi-turn conversation with chatId.""" + +from fastgpt_client import ChatClient +from dotenv import load_dotenv +import os + +load_dotenv() + +API_KEY = os.getenv("API_KEY") +BASE_URL = os.getenv("BASE_URL") + + +def multi_turn_conversation(): + """Maintain context across multiple turns.""" + with ChatClient(api_key=API_KEY, base_url=BASE_URL) as client: + chat_id = "my_conversation" + + conversation = [ + "Hi, I'm learning Python.", + "What are the main data types in Python?", + "Can you give me an example of a list?", + "How do I add items to a list?", + ] + + for user_message in conversation: + print(f"\nUser: {user_message}") + + response = client.create_chat_completion( + messages=[{"role": "user", "content": user_message}], + chatId=chat_id, + stream=False + ) + response.raise_for_status() + result = response.json() + + ai_message = result['choices'][0]['message']['content'] + print(f"AI: {ai_message}") + + +if __name__ == "__main__": + multi_turn_conversation() +``` + +## Managing Multiple Conversations + +```python +def manage_multiple_conversations(): + """Handle separate conversations for different users.""" + with ChatClient(api_key=API_KEY, base_url=BASE_URL) as client: + # Conversation with user A + chat_id_a = "user_123_conversation" + response = client.create_chat_completion( + messages=[{"role": "user", "content": "I like cats"}], + chatId=chat_id_a, + stream=False + ) + + # Conversation with user B + chat_id_b = "user_456_conversation" + response = client.create_chat_completion( + messages=[{"role": "user", "content": "I like dogs"}], + chatId=chat_id_b, + stream=False + ) + + # Continue with user A + response = client.create_chat_completion( + messages=[{"role": "user", "content": "What did I say I like?"}], + chatId=chat_id_a, + stream=False + ) + # Response: "You said you like cats" +``` + +## Retrieving Chat History + +```python +def get_conversation_history(): + """Retrieve and display chat history.""" + with ChatClient(api_key=API_KEY, base_url=BASE_URL) as client: + app_id = os.getenv("APP_ID") + + # Get all chat histories + response = client.get_chat_histories( + appId=app_id, + offset=0, + pageSize=20, + source="api" + ) + response.raise_for_status() + data = response.json() + + print("Chat Histories:") + for chat in data['data']['list']: + print(f" - {chat['title']}: {chat['chatId']}") + + # Get records for a specific chat + chat_id = data['data']['list'][0]['chatId'] + records_response = client.get_chat_records( + appId=app_id, + chatId=chat_id, + offset=0, + pageSize=10 + ) + records_response.raise_for_status() + records_data = records_response.json() + + print(f"\nRecords for {chat_id}:") + for record in records_data['data']['list']: + content = record.get('content', {}) + text = content.get('text', 'N/A') + print(f" - {text[:50]}...") +``` + +## Updating Chat Title + +```python +def update_chat_title(): + """Update the title of a chat.""" + with ChatClient(api_key=API_KEY, base_url=BASE_URL) as client: + app_id = os.getenv("APP_ID") + chat_id = "my_conversation" + + # Update the chat title + response = client.update_chat_history( + appId=app_id, + chatId=chat_id, + customTitle="Python Learning Session" + ) + response.raise_for_status() + print("Chat title updated!") +``` + +## Pinning a Chat + +```python +def pin_chat(): + """Pin a chat to the top.""" + with ChatClient(api_key=API_KEY, base_url=BASE_URL) as client: + app_id = os.getenv("APP_ID") + chat_id = "my_conversation" + + # Pin the chat + response = client.update_chat_history( + appId=app_id, + chatId=chat_id, + top=True + ) + response.raise_for_status() + print("Chat pinned!") +``` + +## Deleting a Chat + +```python +def delete_chat(): + """Delete a chat conversation.""" + with ChatClient(api_key=API_KEY, base_url=BASE_URL) as client: + app_id = os.getenv("APP_ID") + chat_id = "old_conversation" + + # Delete the chat + response = client.delete_chat_history( + appId=app_id, + chatId=chat_id + ) + response.raise_for_status() + print("Chat deleted!") +``` + +## Best Practices + +1. **Use meaningful chat IDs** - Include user IDs or session IDs +2. **Store chat IDs** - Keep them in your database for later retrieval +3. **Handle missing chats** - Chat IDs may expire or be deleted +4. **Clean up old chats** - Delete or archive conversations you no longer need + +```python +def robust_conversation(): + """Handle chatId gracefully.""" + with ChatClient(api_key=API_KEY, base_url=BASE_URL) as client: + chat_id = "user_123_session_456" + + try: + response = client.create_chat_completion( + messages=[{"role": "user", "content": "Hello"}], + chatId=chat_id, + stream=False + ) + response.raise_for_status() + result = response.json() + + # Store the chatId for future use + returned_chat_id = result.get('chatId', chat_id) + print(f"Chat ID: {returned_chat_id}") + + except Exception as e: + # Start a new conversation if this one fails + print(f"Error with chatId: {e}") + print("Starting new conversation...") + # Create new chat without chatId +``` + +## See Also + +- [ChatClient API](../api/chat_client.md) - Complete method reference +- [Variables](variables.md) - Using template variables +- [Streaming](streaming.md) - Streaming with context diff --git a/docs/examples/streaming.md b/docs/examples/streaming.md new file mode 100644 index 0000000..bdbf9be --- /dev/null +++ b/docs/examples/streaming.md @@ -0,0 +1,233 @@ +# Streaming Example + +Learn how to use streaming responses with the FastGPT Python SDK. + +## Why Use Streaming? + +Streaming allows you to: + +- **Display real-time responses** - Show text as it's generated +- **Reduce perceived latency** - Users see content immediately +- **Better user experience** - More interactive and engaging + +## Basic Streaming + +```python +import json +from fastgpt_client import ChatClient + +with ChatClient(api_key="fastgpt-xxxxx") as client: + response = client.create_chat_completion( + messages=[{"role": "user", "content": "Tell me a short story"}], + stream=True + ) + + print("Story: ", end="", flush=True) + for line in response.iter_lines(): + if line.startswith("data:"): + data = line[5:].strip() + if data and data != "[DONE]": + chunk = json.loads(data) + if "choices" in chunk and chunk["choices"]: + delta = chunk["choices"][0].get("delta", {}) + content = delta.get("content", "") + if content: + print(content, end="", flush=True) + print() +``` + +## Complete Streaming Example + +```python +"""Streaming chat completion example.""" + +import json +from fastgpt_client import ChatClient +from dotenv import load_dotenv +import os + +load_dotenv() + +API_KEY = os.getenv("API_KEY") +BASE_URL = os.getenv("BASE_URL") + + +def stream_chat(): + """Simple streaming chat completion.""" + with ChatClient(api_key=API_KEY, base_url=BASE_URL) as client: + response = client.create_chat_completion( + messages=[{"role": "user", "content": "Tell me a short story about AI"}], + stream=True + ) + + print("\n=== Streaming Response ===\n") + for line in response.iter_lines(): + if line.startswith("data:"): + data = line[5:].strip() + if data and data != "[DONE]": + chunk = json.loads(data) + if "choices" in chunk and chunk["choices"]: + delta = chunk["choices"][0].get("delta", {}) + content = delta.get("content", "") + if content: + print(content, end="", flush=True) + print("\n") + + +def stream_with_progress(): + """Stream with progress indicator.""" + with ChatClient(api_key=API_KEY, base_url=BASE_URL) as client: + response = client.create_chat_completion( + messages=[{"role": "user", "content": "Explain quantum computing"}], + stream=True + ) + + print("\n=== Streaming with Progress ===\n") + token_count = 0 + for line in response.iter_lines(): + if line.startswith("data:"): + data = line[5:].strip() + if data and data != "[DONE]": + chunk = json.loads(data) + if "choices" in chunk and chunk["choices"]: + delta = chunk["choices"][0].get("delta", {}) + content = delta.get("content", "") + if content: + print(content, end="", flush=True) + token_count += 1 + print(f"\n\nTotal tokens: {token_count}") + + +def stream_with_buffer(): + """Stream with word buffering (print complete words only).""" + with ChatClient(api_key=API_KEY, base_url=BASE_URL) as client: + response = client.create_chat_completion( + messages=[{"role": "user", "content": "What is machine learning?"}], + stream=True + ) + + print("\n=== Buffered Streaming ===\n") + buffer = "" + for line in response.iter_lines(): + if line.startswith("data:"): + data = line[5:].strip() + if data and data != "[DONE]": + chunk = json.loads(data) + if "choices" in chunk and chunk["choices"]: + delta = chunk["choices"][0].get("delta", {}) + content = delta.get("content", "") + if content: + buffer += content + # Print complete words + if " " in buffer: + parts = buffer.split(" ", 1) + print(parts[0] + " ", end="", flush=True) + buffer = parts[1] if len(parts) > 1 else "" + # Print remaining content + if buffer: + print(buffer) + print() + + +if __name__ == "__main__": + try: + stream_chat() + except Exception as e: + print(f"Error: {e}") + + try: + stream_with_progress() + except Exception as e: + print(f"Error: {e}") + + try: + stream_with_buffer() + except Exception as e: + print(f"Error: {e}") +``` + +## Async Streaming + +```python +import asyncio +import json +from fastgpt_client import AsyncChatClient + +async def stream_async(): + """Async streaming example.""" + async with AsyncChatClient(api_key="fastgpt-xxxxx") as client: + response = await client.create_chat_completion( + messages=[{"role": "user", "content": "Tell me about async/await"}], + stream=True + ) + + async for line in response.aiter_lines(): + if line.startswith("data:"): + data = line[5:].strip() + if data and data != "[DONE]": + chunk = json.loads(data) + if "choices" in chunk and chunk["choices"]: + delta = chunk["choices"][0].get("delta", {}) + content = delta.get("content", "") + if content: + print(content, end="", flush=True) + +asyncio.run(stream_async()) +``` + +## Streaming Event Types + +FastGPT sends multiple SSE (Server-Sent Events) event types. The common ones are: + +- `data` - Standard response chunks (OpenAI-compatible) +- `answer` - Main chat response content +- `fastAnswer` - Quick reply content +- `flowNodeStatus` - Workflow node status updates +- `interactive` - Interactive node prompts +- `error` - Error events + +For more details on event types, see [Streaming Events](../advanced/streaming_events.md). + +## Best Practices + +1. **Always flush output**: Use `flush=True` when printing +2. **Handle connection errors**: Streaming can fail mid-response +3. **Use context managers**: Ensures proper cleanup +4. **Buffer for better formatting**: Consider buffering for word boundaries + +```python +import time + +def robust_stream(): + """Handle streaming errors gracefully.""" + try: + with ChatClient(api_key=API_KEY) as client: + response = client.create_chat_completion( + messages=[{"role": "user", "content": "Hello"}], + stream=True + ) + + for line in response.iter_lines(): + if line: + try: + data = line[5:].strip() if line.startswith("data:") else line + if data and data != "[DONE]": + chunk = json.loads(data) + # Process chunk + if "choices" in chunk and chunk["choices"]: + delta = chunk["choices"][0].get("delta", {}) + content = delta.get("content", "") + if content: + print(content, end="", flush=True) + except json.JSONDecodeError: + continue + + except Exception as e: + print(f"\nStreaming error: {e}") +``` + +## See Also + +- [Streaming Events](../advanced/streaming_events.md) - Advanced SSE event handling +- [Async Usage](async_usage.md) - Async streaming examples +- [Error Handling](../advanced/error_handling.md) - Robust error handling diff --git a/docs/examples/variables.md b/docs/examples/variables.md new file mode 100644 index 0000000..bc07fa0 --- /dev/null +++ b/docs/examples/variables.md @@ -0,0 +1,319 @@ +# Variables Example + +Learn how to use template variables in your FastGPT workflows. + +## What are Variables? + +Variables allow you to dynamically replace placeholders in your FastGPT workflows. This is useful for: + +- **Personalizing responses** - Insert user names, company names, etc. +- **Conditional content** - Show different content based on user input +- **Multi-language support** - Switch languages dynamically +- **Configuration** - Pass settings to your workflow + +## Basic Variable Usage + +```python +from fastgpt_client import ChatClient + +with ChatClient(api_key="fastgpt-xxxxx") as client: + response = client.create_chat_completion( + messages=[{"role": "user", "content": "Introduction"}], + variables={ + "user_name": "Alice", + "company": "Tech Corp", + "language": "English" + }, + stream=False + ) + result = response.json() + print(result['choices'][0]['message']['content']) + # Output might be: "Hello Alice! Welcome to Tech Corp..." +``` + +## Complete Variables Example + +```python +"""Using template variables in FastGPT workflows.""" + +from fastgpt_client import ChatClient +from dotenv import load_dotenv +import os + +load_dotenv() + +API_KEY = os.getenv("API_KEY") +BASE_URL = os.getenv("BASE_URL") + + +def personalized_greeting(): + """Personalized greeting with variables.""" + with ChatClient(api_key=API_KEY, base_url=BASE_URL) as client: + response = client.create_chat_completion( + messages=[{"role": "user", "content": "Greet me"}], + variables={ + "user_name": "Alice", + "time_of_day": "morning", + "language": "English" + }, + stream=False + ) + response.raise_for_status() + result = response.json() + print("Response:", result['choices'][0]['message']['content']) + + +def dynamic_content(): + """Dynamic content based on variables.""" + with ChatClient(api_key=API_KEY, base_url=BASE_URL) as client: + # Generate content for different audiences + audiences = [ + { + "audience": "technical", + "detail_level": "high", + "include_examples": True + }, + { + "audience": "business", + "detail_level": "low", + "include_examples": False + }, + { + "audience": "general", + "detail_level": "medium", + "include_examples": True + } + ] + + for config in audiences: + print(f"\n--- Content for {config['audience']} audience ---") + response = client.create_chat_completion( + messages=[{"role": "user", "content": "Explain AI"}], + variables=config, + stream=False + ) + response.raise_for_status() + result = response.json() + print(result['choices'][0]['message']['content'][:200] + "...") + + +def multi_language(): + """Multi-language support with variables.""" + with ChatClient(api_key=API_KEY, base_url=BASE_URL) as client: + languages = [ + {"language": "English", "user_name": "Alice"}, + {"language": "Spanish", "user_name": "Carlos"}, + {"language": "French", "user_name": "Marie"}, + ] + + for lang_config in languages: + print(f"\n--- Response in {lang_config['language']} ---") + response = client.create_chat_completion( + messages=[{"role": "user", "content": "Say hello"}], + variables=lang_config, + stream=False + ) + response.raise_for_status() + result = response.json() + print(result['choices'][0]['message']['content']) + + +def context_aware_response(): + """Response with contextual variables.""" + with ChatClient(api_key=API_KEY, base_url=BASE_URL) as client: + # User context + user_context = { + "user_name": "Alice", + "user_level": "beginner", + "topic": "Python programming", + "goal": "learn data analysis" + } + + # Generate personalized learning plan + response = client.create_chat_completion( + messages=[ + { + "role": "user", + "content": "Create a learning plan for me" + } + ], + variables=user_context, + stream=False + ) + response.raise_for_status() + result = response.json() + print("Personalized Learning Plan:") + print(result['choices'][0]['message']['content']) + + +def workflow_with_variables(): + """Complete workflow using multiple variables.""" + with ChatClient(api_key=API_KEY, base_url=BASE_URL) as client: + # Scenario: Customer support chat + customer_context = { + "customer_name": "John Doe", + "company": "Acme Corp", + "plan": "enterprise", + "issue_type": "technical", + "urgency": "high", + "language": "English", + "account_id": "12345" + } + + # Generate personalized support response + response = client.create_chat_completion( + messages=[ + { + "role": "user", + "content": "I need help with my account" + } + ], + variables=customer_context, + stream=False + ) + response.raise_for_status() + result = response.json() + print("Support Response:") + print(result['choices'][0]['message']['content']) + + +def streaming_with_variables(): + """Streaming response with variables.""" + import json + + with ChatClient(api_key=API_KEY, base_url=BASE_URL) as client: + response = client.create_chat_completion( + messages=[{"role": "user", "content": "Tell me a story"}], + variables={ + "genre": "science fiction", + "length": "short", + "protagonist": "AI robot" + }, + stream=True + ) + + print("Streaming story: ", end="", flush=True) + for line in response.iter_lines(): + if line.startswith("data:"): + data = line[5:].strip() + if data and data != "[DONE]": + chunk = json.loads(data) + if "choices" in chunk and chunk["choices"]: + delta = chunk["choices"][0].get("delta", {}) + content = delta.get("content", "") + if content: + print(content, end="", flush=True) + print() + + +if __name__ == "__main__": + print("=== Personalized Greeting ===") + try: + personalized_greeting() + except Exception as e: + print(f"Error: {e}") + + print("\n=== Dynamic Content ===") + try: + dynamic_content() + except Exception as e: + print(f"Error: {e}") + + print("\n=== Multi-Language ===") + try: + multi_language() + except Exception as e: + print(f"Error: {e}") + + print("\n=== Context-Aware Response ===") + try: + context_aware_response() + except Exception as e: + print(f"Error: {e}") + + print("\n=== Workflow with Variables ===") + try: + workflow_with_variables() + except Exception as e: + print(f"Error: {e}") + + print("\n=== Streaming with Variables ===") + try: + streaming_with_variables() + except Exception as e: + print(f"Error: {e}") +``` + +## Common Use Cases + +### 1. Personalization + +```python +variables = { + "user_name": "Alice", + "company": "Tech Corp", + "role": "Developer" +} +``` + +### 2. Localization + +```python +variables = { + "language": "Spanish", + "region": "Latin America", + "currency": "USD" +} +``` + +### 3. Conditional Logic + +```python +variables = { + "user_tier": "premium", + "feature_set": "advanced", + "support_level": "24/7" +} +``` + +### 4. Content Generation + +```python +variables = { + "topic": "AI", + "tone": "professional", + "length": "500", + "format": "blog post" +} +``` + +## Best Practices + +1. **Use descriptive variable names** - `user_name` instead of `n` +2. **Validate variables** - Ensure required variables are provided +3. **Document your variables** - Keep a list of expected variables +4. **Use defaults** - Provide fallback values when possible + +```python +def get_variables(user_input): + """Validate and prepare variables.""" + required_vars = ["user_name", "language"] + variables = { + "user_name": user_input.get("name", "Guest"), + "language": user_input.get("language", "English"), + "company": user_input.get("company", ""), + } + + # Validate required variables + missing = [var for var in required_vars if not variables.get(var)] + if missing: + raise ValueError(f"Missing required variables: {missing}") + + return variables +``` + +## See Also + +- [ChatClient API](../api/chat_client.md) - Complete API reference +- [Chat Context](chat_context.md) - Managing conversation context +- [Streaming](streaming.md) - Streaming responses diff --git a/docs/getting_started/authentication.md b/docs/getting_started/authentication.md new file mode 100644 index 0000000..4c0cd7b --- /dev/null +++ b/docs/getting_started/authentication.md @@ -0,0 +1,126 @@ +# Authentication + +FastGPT API uses API keys for authentication. This guide covers how to securely manage and use your API keys with the SDK. + +## API Key Format + +FastGPT API keys typically start with `fastgpt-`: + +``` +fastgpt-xxxxxxxxxxxxxxxxxxxxxx +``` + +## Setting Your API Key + +### Method 1: Directly in Code (Not Recommended) + +```python +from fastgpt_client import ChatClient + +client = ChatClient(api_key="fastgpt-xxxxx") +``` + +!!! warning + Never commit API keys to version control! + +### Method 2: Environment Variables (Recommended) + +Create a `.env` file: + +```bash +API_KEY=fastgpt-xxxxx +BASE_URL=http://localhost:3000 +``` + +Use `python-dotenv` to load it: + +```python +import os +from dotenv import load_dotenv +from fastgpt_client import ChatClient + +load_dotenv() + +client = ChatClient( + api_key=os.getenv("API_KEY"), + base_url=os.getenv("BASE_URL") +) +``` + +Add `.env` to your `.gitignore`: + +``` +.env +``` + +### Method 3: System Environment Variables + +Set the environment variable in your shell: + +```bash +# Linux/macOS +export FASTGPT_API_KEY="fastgpt-xxxxx" +export FASTGPT_BASE_URL="http://localhost:3000" + +# Windows (Command Prompt) +set FASTGPT_API_KEY=fastgpt-xxxxx +set FASTGPT_BASE_URL=http://localhost:3000 + +# Windows (PowerShell) +$env:FASTGPT_API_KEY="fastgpt-xxxxx" +$env:FASTGPT_BASE_URL="http://localhost:3000" +``` + +Then use it in Python: + +```python +import os +from fastgpt_client import ChatClient + +client = ChatClient( + api_key=os.getenv("FASTGPT_API_KEY"), + base_url=os.getenv("FASTGPT_BASE_URL", "http://localhost:3000") +) +``` + +## Base URL Configuration + +The default base URL is `http://localhost:3000`. If you're using a different FastGPT instance: + +```python +client = ChatClient( + api_key="fastgpt-xxxxx", + base_url="https://your-fastgpt-instance.com" +) +``` + +## Authentication Errors + +If authentication fails, the SDK raises an `AuthenticationError`: + +```python +from fastgpt_client import ChatClient +from fastgpt_client.exceptions import AuthenticationError + +try: + with ChatClient(api_key="invalid-key") as client: + response = client.create_chat_completion( + messages=[{"role": "user", "content": "Hello"}] + ) +except AuthenticationError as e: + print(f"Authentication failed: {e}") +``` + +## Security Best Practices + +1. **Never expose API keys** in client-side code (browsers, mobile apps) +2. **Use environment variables** to store keys +3. **Rotate keys regularly** for production applications +4. **Use separate keys** for different environments (dev, staging, prod) +5. **Monitor usage** to detect unauthorized access +6. **Commit `.env` to `.gitignore`** to prevent accidental commits + +## Next Steps + +- [Quick Start](quick_start.md) - Start using the SDK +- [Error Handling](../advanced/error_handling.md) - Learn to handle errors properly diff --git a/docs/getting_started/installation.md b/docs/getting_started/installation.md new file mode 100644 index 0000000..011d585 --- /dev/null +++ b/docs/getting_started/installation.md @@ -0,0 +1,45 @@ +# Installation + +## Requirements + +- Python 3.8 or higher +- A FastGPT API key + +## Install with pip + +```bash +pip install fastgpt-client +``` + +## Install for Development + +If you want to contribute to the SDK or run tests: + +```bash +# Clone the repository +git clone https://github.com/yourusername/fastgpt-python-sdk.git +cd fastgpt-python-sdk + +# Install in development mode +pip install -e ".[dev]" +``` + +### Development Dependencies + +The `[dev]` extra includes: +- `pytest` - Testing framework +- `ruff` - Linting and formatting +- `httpx` - HTTP client (already included) +- `python-dotenv` - Environment variable management + +## Verify Installation + +```python +from fastgpt_client import ChatClient, AppClient +print("FastGPT SDK installed successfully!") +``` + +## Next Steps + +- [Quick Start Guide](quick_start.md) - Learn the basics +- [Authentication](authentication.md) - Set up your API key diff --git a/docs/getting_started/quick_start.md b/docs/getting_started/quick_start.md new file mode 100644 index 0000000..3598b2f --- /dev/null +++ b/docs/getting_started/quick_start.md @@ -0,0 +1,147 @@ +# Quick Start + +This guide will help you get started with the FastGPT Python SDK in just a few minutes. + +## 1. Get Your API Key + +You'll need a FastGPT API key to use the SDK. You can obtain one from your FastGPT instance: + +1. Log in to your FastGPT instance +2. Navigate to Settings → API Keys +3. Create a new API key +4. Copy the key (it will start with `fastgpt-`) + +## 2. Basic Chat Completion + +The simplest way to use the SDK is with a basic chat completion: + +```python +from fastgpt_client import ChatClient + +# Initialize the client +with ChatClient( + api_key="fastgpt-xxxxx", + base_url="http://localhost:3000" +) as client: + # Send a message + response = client.create_chat_completion( + messages=[{"role": "user", "content": "Hello! What's AI?"}], + stream=False + ) + response.raise_for_status() + result = response.json() + + # Print the response + print(result['choices'][0]['message']['content']) +``` + +## 3. Streaming Responses + +For real-time responses, use streaming: + +```python +import json + +with ChatClient(api_key="fastgpt-xxxxx") as client: + response = client.create_chat_completion( + messages=[{"role": "user", "content": "Tell me a short story"}], + stream=True + ) + + for line in response.iter_lines(): + if line.startswith("data:"): + data = line[5:].strip() + if data and data != "[DONE]": + chunk = json.loads(data) + if "choices" in chunk and chunk["choices"]: + delta = chunk["choices"][0].get("delta", {}) + content = delta.get("content", "") + if content: + print(content, end="", flush=True) +``` + +## 4. Using Context (chatId) + +To maintain a conversation across multiple requests, use `chatId`: + +```python +with ChatClient(api_key="fastgpt-xxxxx") as client: + chat_id = "my_conversation_123" + + # First message + response = client.create_chat_completion( + messages=[{"role": "user", "content": "What's AI?"}], + chatId=chat_id, + stream=False + ) + + # Second message (continues the conversation) + response = client.create_chat_completion( + messages=[{"role": "user", "content": "Tell me more"}], + chatId=chat_id, # Same chatId maintains context + stream=False + ) +``` + +## 5. Using Variables + +You can use template variables in your FastGPT workflows: + +```python +with ChatClient(api_key="fastgpt-xxxxx") as client: + response = client.create_chat_completion( + messages=[{"role": "user", "content": "Hello [name]!"}], + variables={"name": "Alice"}, # Replaces [name] placeholder + stream=False + ) +``` + +## 6. Get Chat Histories + +Retrieve your chat histories: + +```python +with ChatClient(api_key="fastgpt-xxxxx") as client: + histories = client.get_chat_histories( + appId="your-app-id", + offset=0, + pageSize=20, + source="api" + ) + histories.raise_for_status() + data = histories.json() + + for chat in data['data']['list']: + print(f"{chat['title']}: {chat['chatId']}") +``` + +## Environment Variables + +For better security, use environment variables for your API key: + +```bash +# .env file +API_KEY=fastgpt-xxxxx +BASE_URL=http://localhost:3000 +``` + +```python +import os +from dotenv import load_dotenv +from fastgpt_client import ChatClient + +load_dotenv() + +with ChatClient( + api_key=os.getenv("API_KEY"), + base_url=os.getenv("BASE_URL") +) as client: + # Your code here + pass +``` + +## Next Steps + +- [Authentication](authentication.md) - Learn more about securing your API key +- [API Reference](../api/overview.md) - Explore all available methods +- [Examples](../examples/basic_usage.md) - More usage examples diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..35a31eb --- /dev/null +++ b/docs/index.md @@ -0,0 +1,61 @@ +# FastGPT Python SDK + +The official Python SDK for interacting with FastGPT's OpenAPI. + +## Overview + +FastGPT Python SDK is a client library that provides a simple, Pythonic interface to FastGPT's powerful AI platform. It supports both synchronous and asynchronous operations, with built-in retry logic, error handling, and connection pooling. + +### Key Features + +- **Easy to Use**: Simple, intuitive API design following Python best practices +- **OpenAI-Compatible**: The `/api/v1/chat/completions` endpoint is fully OpenAI-compatible +- **Context Manager Support**: Automatic resource cleanup with `with` statements +- **Async Support**: Full async/await support for high-performance applications +- **Built-in Retry Logic**: Automatic retries with configurable backoff +- **Streaming**: Support for streaming responses +- **Chat Context**: Maintain conversation history with `chatId` +- **Template Variables**: Dynamic content substitution with variables + +## Quick Example + +```python +from fastgpt_client import ChatClient + +# Basic chat completion +with ChatClient(api_key="fastgpt-xxxxx") as client: + response = client.create_chat_completion( + messages=[{"role": "user", "content": "Hello!"}], + stream=False + ) + result = response.json() + print(result['choices'][0]['message']['content']) +``` + +## Installation + +```bash +pip install fastgpt-client +``` + +## Client Types + +| Client | Description | +|--------|-------------| +| `ChatClient` | For chat operations and conversation management | +| `AppClient` | For app analytics and logs | +| `AsyncChatClient` | Async version of ChatClient | +| `AsyncAppClient` | Async version of AppClient | + +## Documentation + +- **[Getting Started](getting_started/installation.md)** - Installation and setup +- **[API Reference](api/overview.md)** - Complete API documentation +- **[Examples](examples/basic_usage.md)** - Usage examples +- **[Advanced Topics](advanced/error_handling.md)** - Advanced features + +## Links + +- [FastGPT Documentation](https://doc.fastgpt.io/) +- [Chat API Documentation](https://doc.fastgpt.io/docs/introduction/development/openapi/chat) +- [GitHub Repository](https://github.com/yourusername/fastgpt-python-sdk) diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..fd80bc9 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,98 @@ +site_name: FastGPT Python SDK +site_description: Python SDK for FastGPT OpenAPI +site_author: FastGPT +repo_url: https://github.com/yourusername/fastgpt-python-sdk +repo_name: fastgpt-python-sdk + +theme: + name: material + palette: + - scheme: default + primary: indigo + accent: indigo + toggle: + icon: material/brightness-7 + name: Switch to dark mode + - scheme: slate + primary: indigo + accent: indigo + toggle: + icon: material/brightness-4 + name: Switch to light mode + features: + - search.suggest + - search.highlight + - navigation.tabs + - navigation.sections + - navigation.expand + - navigation.top + - content.code.copy + - content.tabs.link + icon: + repo: fontawesome/brands/github + +plugins: + - search + - mkdocstrings: + handlers: + python: + options: + docstring_style: google + show_source: true + show_root_heading: true + show_root_members_full_path: false + show_object_full_path: false + show_signature_annotations: true + signature_crossrefs: true + merge_init_into_class: true + +markdown_extensions: + - pymdownx.highlight: + anchor_linenums: true + line_spans: __span + pygments_lang_class: true + - pymdownx.inlinehilite + - pymdownx.snippets + - pymdownx.superfences: + custom_fences: + - name: mermaid + class: mermaid + format: !!python/name:pymdownx.superfences.fence_code_format + - pymdownx.tabbed: + alternate_style: true + - admonition + - pymdownx.details + - attr_list + - md_in_html + +extra: + social: + - icon: fontawesome/brands/github + link: https://github.com/yourusername/fastgpt-python-sdk + - icon: fontawesome/solid/globe + link: https://doc.fastgpt.io/ + +nav: + - Home: index.md + - Getting Started: + - Installation: getting_started/installation.md + - Quick Start: getting_started/quick_start.md + - Authentication: getting_started/authentication.md + - API Reference: + - Overview: api/overview.md + - ChatClient: api/chat_client.md + - AppClient: api/app_client.md + - Async Clients: api/async_clients.md + - Exceptions: api/exceptions.md + - Examples: + - Basic Usage: examples/basic_usage.md + - Streaming: examples/streaming.md + - Async Usage: examples/async_usage.md + - Chat Context: examples/chat_context.md + - Variables: examples/variables.md + - Advanced Topics: + - Error Handling: advanced/error_handling.md + - Streaming Events: advanced/streaming_events.md + - Rate Limiting: advanced/rate_limiting.md + - Detail Mode: advanced/detail_mode.md + - Development: development.md