search-codegen¶
第1章 · Agent 基础知识 · 配套项目
chapter1/search-codegen
项目说明¶
GPT-5 Native Tools Agent¶
An advanced AI agent leveraging GPT-5's native web_search and code_interpreter tools through the OpenRouter API, matching the exact implementation pattern from production Go code. This agent can search the internet for real-time information and run code for deep analysis using GPT-5's built-in capabilities, realizing the "搜索 → 阅读 → 分析 → 再搜索" Deep Research loop.
🌟 Features¶
- Native Tool Support: Utilizes GPT-5's built-in
web_searchandcode_interpretertools with OpenRouter-specific format - OpenRouter Integration: Exact API format matching production Go implementation
- Web Search Capability:
- Real-time internet search for current information
- Configurable search context size and user location
- Reasoning Levels: Support for low, medium, and high reasoning effort
- Interactive CLI: User-friendly command-line interface with reasoning controls
- Agent Chaining: Chain multiple requests for complex workflows
- Comprehensive Testing: Test suite demonstrating various use cases
📋 Prerequisites¶
- Python 3.8 or higher
- OpenRouter API key (get one at https://openrouter.ai/keys)
- Internet connection for web search functionality
🚀 Quick Start¶
1. Installation¶
# Clone or navigate to the project directory
cd projects/week1/search-codegen
# Install dependencies
pip install -r requirements.txt
# Copy environment template
cp env.example .env
# Edit .env and add your OpenRouter API key
# OPENROUTER_API_KEY=sk-or-v1-your-key-here
2. Configuration¶
Edit .env file with your settings:
OPENROUTER_API_KEY=sk-or-v1-your-api-key-here
MODEL_NAME=openai/gpt-5.6-sol
DEFAULT_TEMPERATURE=0.3
DEFAULT_MAX_TOKENS=4000
This experiment uses OpenRouter as its primary backend, so no fallback is needed. The same
OPENROUTER_API_KEYdoubles as the universal fallback for the other chapter1 experiments (context,learning-from-experience,web-search-agent) when their direct provider key is missing.
3. Run the Agent¶
Interactive Mode (Recommended)¶
Single Request Mode¶
Run Tests¶
命令行参数(CLI)¶
本实验对应书中 实验 1.3 ★:GPT-5.6 原生 Deep Research 能力,演示模型如何自主组合 web_search(网络搜索)与 code_interpreter(代码解释器)两个原生工具,完成“搜索 → 阅读 → 分析 → 再搜索”的迭代研究。运行 python main.py --help 查看中文帮助。
| 参数 | 说明 | 默认值 |
|---|---|---|
--mode |
运行模式:interactive 交互 / single 单次 / test 测试 |
interactive |
--request |
single / --dry-run 模式下的任务或查询内容 |
— |
--model |
覆盖模型名称 | 配置中的 MODEL_NAME |
--reasoning |
推理力度 Reasoning Effort(low/medium/high) |
low |
--verbosity |
输出详略程度 Verbosity(low/medium/high) |
跟随模型 |
--no-tools |
禁用原生工具 | 启用 |
--output |
将完整结果(含轨迹 / 请求体)保存为 JSON | — |
--dry-run |
离线组装并打印请求体,不联网、无需 API Key | 关闭 |
--test |
test 模式下运行指定用例 |
运行全部 |
Reasoning Effort 与 Verbosity 是书中强调的两个 GPT-5 原生参数:前者调节思考深度,后者控制回答详略。二者都已通过 CLI 暴露,并原样注入到发送给模型的请求体中。
示例:
# 书中示例任务:东盟 10 国首都最近的一对(搜索坐标 + 代码计算大圆距离)
python main.py --mode single --request "东盟 10 国首都之间距离最近的两个首都是?给出详细分析推理过程。" --reasoning high
# 书中示例任务:比特币技术分析(多源实时数据 + 指标计算)
python main.py --mode single --request "搜索比特币最近一个月走势,计算 MA、RSI、MACD 等技术指标" --verbosity high --output btc.json
离线查看请求体(dry-run)¶
无需 API Key 即可查看“模型即 Agent”范式下真正发送给模型的请求——包括两个原生工具的定义、reasoning 与 verbosity 参数。这直观展示了原生工具调用的结构,也便于调试:
输出的请求体中,tools 数组同时包含 web_search 和 code_interpreter,reasoning.effort 与 verbosity 反映所选档位——正是书中所述的原生工具 + 推理/详略参数的组合。
🛠️ Usage Examples¶
Example 1: Web Search Only¶
from agent import GPT5NativeAgent
from config import Config
agent = GPT5NativeAgent(
api_key=Config.OPENROUTER_API_KEY,
base_url=Config.OPENROUTER_BASE_URL
)
result = agent.process_request(
"What are the latest developments in quantum computing?",
use_tools=True
)
print(result["response"])
Example 2: Web Search with High Reasoning¶
result = agent.process_request(
"Analyze the implications of quantum computing on encryption",
use_tools=True,
reasoning_effort="high"
)
Example 3: Web Search with Analysis¶
result = agent.process_request(
"""Search for current Bitcoin price and market data,
then analyze the volatility and predict trends""",
use_tools=True,
reasoning_effort="medium"
)
Example 4: Search and Analyze Method¶
analysis_code = """
import statistics
# Process search results
prices = [45000, 46000, 45500, 47000, 46500]
volatility = statistics.stdev(prices)
print(f"Volatility: ${volatility:.2f}")
"""
result = agent.search_and_analyze(
topic="Current cryptocurrency market conditions",
analysis_code=analysis_code
)
📁 Project Structure¶
search-codegen/
├── agent.py # Core GPT-5 agent implementation
├── config.py # Configuration management
├── main.py # Interactive CLI and entry point
├── test_agent.py # Comprehensive test suite
├── env.example # Environment variables template
├── requirements.txt # Python dependencies
└── README.md # This file
🔧 OpenRouter Tool Format¶
web_search Tool Structure¶
The web_search tool uses OpenRouter's specific format:
{
"type": "web_search",
"search_context_size": "medium",
"user_location": {
"type": "approximate",
"country": "US"
}
}
Reasoning Configuration¶
Supports configurable reasoning effort: - low: Fast responses with basic reasoning - medium: Balanced reasoning and response time - high: Deep reasoning for complex queries
🧪 Testing¶
The test suite includes comprehensive test cases:
- Basic Web Search: Test internet search capabilities
- Web Search with Analysis: Search with analytical insights
- Complex Research: Deep research with high reasoning
- Search and Code: Search with code generation
- Reasoning Comparison: Compare different reasoning levels
- Search and Analyze Method: Convenience method testing
- Agent Chain: Multi-step workflow
Run specific tests:
Available test names: basic, analysis, complex, code, reasoning, search_analyze, chain
🎯 Interactive CLI Commands¶
When running in interactive mode, the following commands are available:
/help- Show help message/clear- Clear conversation history/history- Show conversation history/tools- Toggle tools on/off/search- Enter web search mode/code- Enter code generation mode/analyze- Combined search + analysis mode/config- Show current configuration/reasoning- Set reasoning effort level/exit- Exit the application
⚙️ Configuration Options¶
| Variable | Description | Default |
|---|---|---|
OPENROUTER_API_KEY |
Your OpenRouter API key | Required |
MODEL_NAME |
GPT-5 model identifier | openai/gpt-5.6-sol |
DEFAULT_TEMPERATURE |
Response randomness (0-1) | 0.3 |
DEFAULT_MAX_TOKENS |
Maximum response length | 4000 |
DEFAULT_TOOL_CHOICE |
Tool selection strategy | auto |
LOG_LEVEL |
Logging verbosity | INFO |
🤝 API Integration¶
This agent uses the OpenRouter API to access GPT-5. OpenRouter provides: - Unified API for multiple models - Automatic fallbacks for reliability - Usage tracking and analytics - Competitive pricing
Learn more at OpenRouter Documentation
📊 Token Usage¶
The agent tracks token usage for each request: - Prompt tokens: Input token count - Completion tokens: Output token count - Total tokens: Combined usage
Monitor costs based on OpenRouter's pricing: - Input: $1.25 per million tokens - Output: $10 per million tokens
🐛 Troubleshooting¶
API Key Issues¶
Rate Limiting¶
Adjust RATE_LIMIT_RPM in .env if encountering rate limits
Tool Errors¶
- Ensure
use_tools=Truewhen callingprocess_request - Set
tool_choice="required"to force tool usage
📝 License¶
This project is part of the AI Agent实战训练营 curriculum.
🔗 Resources¶
👥 Support¶
For issues or questions: 1. Check the troubleshooting section 2. Review test cases for usage examples 3. Consult the OpenRouter documentation
Built with GPT-5's native capabilities via OpenRouter API 🚀
源代码¶
agent.py¶
"""
GPT-5 Native Tools Agent
An advanced agent leveraging GPT-5's native web_search and code_interpreter tools via OpenRouter API.
"""
import json
import os
from typing import List, Dict, Any, Optional, Literal
from openai import OpenAI
import logging
from dataclasses import dataclass
from enum import Enum
import requests
# Set up logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
def _reasoning_safe_temperature(model, requested=1.0):
"""Reasoning models (Kimi K3, GPT-5, ...) only accept temperature=1.
Return 1 for those; otherwise the requested value so non-reasoning
providers (Doubao, DeepSeek, older Moonshot) are unchanged."""
m = str(model or "").lower().replace("/", "-")
return 1 if ("kimi-k3" in m or "gpt-5" in m) else requested
class ToolType(Enum):
"""Enum for GPT-5 native tool types"""
WEB_SEARCH = "web_search"
CODE_INTERPRETER = "code_interpreter"
@dataclass
class ToolResult:
"""Container for tool execution results"""
tool_type: ToolType
success: bool
result: Any
error: Optional[str] = None
class GPT5NativeAgent:
"""
GPT-5 Agent with Native Tool Support
This agent uses GPT-5's native web_search and code_interpreter capabilities
through the OpenRouter API. These tools are built into GPT-5 and don't require
manual implementation.
Based on OpenAI's native tool support:
- web_search: Native internet search capability
- code_interpreter: Built-in code execution environment
"""
def __init__(
self,
api_key: str,
base_url: str = "https://openrouter.ai/api/v1",
model: str = "openai/gpt-5.6-sol"
):
"""
Initialize the GPT-5 agent with OpenRouter API
Args:
api_key: OpenRouter API key
base_url: OpenRouter API base URL
model: Model identifier (default: openai/gpt-5.6-sol)
"""
self.api_key = api_key
self.base_url = base_url
self.model = model
self.conversation_history: List[Dict[str, Any]] = []
self.system_prompt = self._create_system_prompt()
def _create_system_prompt(self) -> str:
"""
Create the system prompt for the agent
Returns:
System prompt string
"""
return """You are an advanced AI assistant powered by GPT-5 with native tool capabilities.
You have access to two powerful native tools:
1. **web_search**: Use this to search the internet for real-time information, current events,
documentation, or any information not in your training data.
2. **code_interpreter**: Use this to execute Python code, perform calculations, data analysis,
generate visualizations, or solve computational problems.
Guidelines:
- Analyze the user's request carefully to determine which tools to use
- You can use multiple tools in sequence or combination to provide comprehensive answers
- When using code_interpreter, write clear, well-commented code
- When using web_search, search for authoritative and recent sources
- Always synthesize information from tools into clear, actionable responses
- Be proactive in using tools when they would enhance your answer quality
Remember: These are native tools built into your capabilities, use them naturally as part of your reasoning process."""
def _build_openrouter_request(
self,
messages: List[Dict[str, Any]],
use_tools: bool = True,
reasoning_effort: str = "low",
stream: bool = False,
verbosity: Optional[str] = None
) -> Dict[str, Any]:
"""
Build the OpenRouter-specific request format matching the Go implementation
Args:
messages: Conversation messages
use_tools: Whether to enable tools
reasoning_effort: Reasoning effort level (low, medium, high)
stream: Whether to stream the response
verbosity: Output verbosity level (low, medium, high). GPT-5's native
parameter controlling how detailed the answer is. None keeps the
model default.
Returns:
Request dictionary
"""
request = {
"model": self.model,
"messages": messages,
"stream": stream
}
if use_tools:
# Match the exact Go implementation structure
request["tools"] = [
{
"type": "web_search",
"search_context_size": "medium",
"user_location": {
"type": "approximate",
"country": "US"
}
},
{
"type": "code_interpreter",
"container": {"type": "auto"}
},
]
request["tool_choice"] = "auto"
request["parallel_tool_calls"] = True
# Add reasoning configuration
request["reasoning"] = {
"effort": reasoning_effort,
"generate_summary": False
}
# Add verbosity configuration (GPT-5 native parameter, only when set)
if verbosity:
request["verbosity"] = verbosity
request["background"] = False
return request
def process_request(
self,
user_request: str,
use_tools: bool = True,
tool_choice: Literal["auto", "none", "required"] = "auto",
temperature: float = 0.3,
max_tokens: Optional[int] = None,
reasoning_effort: str = "low",
verbosity: Optional[str] = None,
dry_run: bool = False
) -> Dict[str, Any]:
"""
Process a user request with optional tool usage (OpenRouter format)
Args:
user_request: The user's request or question
use_tools: Whether to enable native tools
tool_choice: Tool selection strategy (for compatibility, internally uses "auto")
temperature: Response temperature (0-1)
max_tokens: Maximum tokens in response
reasoning_effort: Reasoning effort level (low, medium, high)
verbosity: Output verbosity level (low, medium, high); None keeps default
dry_run: If True, build and return the request body WITHOUT calling the
API. Useful for inspecting the native-tool request offline.
Returns:
Dictionary containing the response and metadata
"""
# Add system prompt if this is the first message
if not self.conversation_history:
self.conversation_history.append({
"role": "system",
"content": self.system_prompt
})
# Add user message to history
self.conversation_history.append({
"role": "user",
"content": user_request
})
logger.info(f"Processing request: {user_request[:100]}...")
logger.info(f"Using OpenRouter format with reasoning effort: {reasoning_effort}")
try:
# Build the OpenRouter-specific request
request_body = self._build_openrouter_request(
messages=self.conversation_history,
use_tools=use_tools,
reasoning_effort=reasoning_effort,
stream=False,
verbosity=verbosity
)
# Add temperature and max_tokens if specified
if temperature is not None:
request_body["temperature"] = _reasoning_safe_temperature(self.model, temperature)
if max_tokens:
request_body["max_tokens"] = max_tokens
logger.info(f"Request body: {json.dumps(request_body, indent=2)}")
# Dry-run: return the assembled request without hitting the network
if dry_run:
logger.info("Dry-run mode: returning request body without calling the API")
return {
"success": True,
"dry_run": True,
"response": None,
"request": request_body,
"tool_calls": [],
"model": self.model
}
# Make the API call directly using requests (matching Go implementation)
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {self.api_key}"
}
response = requests.post(
f"{self.base_url}/chat/completions",
headers=headers,
json=request_body,
timeout=600
)
logger.info(f"Response status: {response.status_code}")
if response.status_code != 200:
error_msg = f"API error (status {response.status_code}): {response.text}"
logger.error(error_msg)
return {
"success": False,
"error": error_msg,
"response": None,
"tool_calls": []
}
response_data = response.json()
# Log usage information
if "usage" in response_data:
usage = response_data["usage"]
logger.info(f"GPT-5 OpenRouter Usage - Input: {usage.get('input_tokens', 0)} tokens "
f"(cached: {usage.get('input_tokens_details', {}).get('cached_tokens', 0)}), "
f"Output: {usage.get('output_tokens', 0)} tokens "
f"(reasoning: {usage.get('output_tokens_details', {}).get('reasoning_tokens', 0)}), "
f"Total: {usage.get('total_tokens', 0)}")
# Extract the message
message_content = None
if response_data.get("choices") and len(response_data["choices"]) > 0:
message = response_data["choices"][0].get("message", {})
message_content = message.get("content", "")
# Add assistant response to history
if message_content:
self.conversation_history.append({
"role": "assistant",
"content": message_content
})
# Prepare the result
result = {
"success": True,
"response": message_content or "No response generated",
"tool_calls": [], # GPT-5 handles tools internally
"usage": response_data.get("usage", {}),
"model": self.model
}
logger.info("Request processed successfully")
return result
except Exception as e:
logger.error(f"Error processing request: {str(e)}")
return {
"success": False,
"error": str(e),
"response": None,
"tool_calls": []
}
def search_and_analyze(self, topic: str, analysis_code: Optional[str] = None) -> Dict[str, Any]:
"""
Combine web search with code analysis
This method demonstrates using both native tools together:
1. Search for information on a topic
2. Optionally analyze the results with code
Args:
topic: Topic to search and analyze
analysis_code: Optional Python code to analyze the search results
Returns:
Combined results from both tools
"""
# Construct a request that uses both tools
if analysis_code:
request = f"""Please help me with the following task:
1. First, search the web for current information about: {topic}
2. Then, analyze the findings using this code:
```python
{analysis_code}
Provide a comprehensive response combining the search results and code analysis.""" else: request = f"""Search for current information about: {topic}
Then provide a data-driven analysis of the findings, using code to process or visualize any quantitative information if relevant."""
return self.process_request(request, use_tools=True, reasoning_effort="medium")
def clear_history(self):
"""Clear the conversation history"""
self.conversation_history = []
logger.info("Conversation history cleared")
def get_history(self) -> List[Dict[str, Any]]:
"""Get the current conversation history"""
return self.conversation_history.copy()
def set_system_prompt(self, prompt: str):
"""
Update the system prompt
Args:
prompt: New system prompt
"""
self.system_prompt = prompt
if self.conversation_history and self.conversation_history[0]["role"] == "system":
self.conversation_history[0]["content"] = prompt
logger.info("System prompt updated")
class GPT5AgentChain: """ Chain multiple GPT-5 agent calls for complex workflows """
def __init__(self, agent: GPT5NativeAgent):
"""
Initialize the agent chain
Args:
agent: GPT5NativeAgent instance
"""
self.agent = agent
self.chain_results = []
def add_step(self, request: str, **kwargs) -> 'GPT5AgentChain':
"""
Add a step to the chain
Args:
request: Request for this step
**kwargs: Additional parameters for process_request
Returns:
Self for chaining
"""
result = self.agent.process_request(request, **kwargs)
self.chain_results.append({
"request": request,
"result": result
})
return self
def execute(self) -> List[Dict[str, Any]]:
"""
Execute the chain and return all results
Returns:
List of all chain results
"""
return self.chain_results
def clear(self):
"""Clear the chain results"""
self.chain_results = []
```
config.py¶
```python """ Configuration for GPT-5 Native Tools Agent """ import os from dotenv import load_dotenv from typing import Optional
Load environment variables¶
load_dotenv() class Config: """Configuration class for GPT-5 Agent""" # API Configuration OPENROUTER_API_KEY: str = os.getenv("OPENROUTER_API_KEY", "") OPENROUTER_BASE_URL: str = os.getenv("OPENROUTER_BASE_URL", "https://openrouter.ai/api/v1") # Model Configuration MODEL_NAME: str = os.getenv("MODEL_NAME", "openai/gpt-5.6-sol") # Request Configuration DEFAULT_TEMPERATURE: float = float(os.getenv("DEFAULT_TEMPERATURE", "0.3")) DEFAULT_MAX_TOKENS: Optional[int] = int(os.getenv("DEFAULT_MAX_TOKENS", "4000")) if os.getenv("DEFAULT_MAX_TOKENS") else None DEFAULT_TOOL_CHOICE: str = os.getenv("DEFAULT_TOOL_CHOICE", "auto") # Logging Configuration LOG_LEVEL: str = os.getenv("LOG_LEVEL", "INFO") LOG_FORMAT: str = "%(asctime)s - %(name)s - %(levelname)s - %(message)s" # Rate Limiting (requests per minute) RATE_LIMIT_RPM: int = int(os.getenv("RATE_LIMIT_RPM", "20")) # Retry Configuration MAX_RETRIES: int = int(os.getenv("MAX_RETRIES", "3")) RETRY_DELAY: float = float(os.getenv("RETRY_DELAY", "1.0")) # Tool-specific Configuration WEB_SEARCH_MAX_RESULTS: int = int(os.getenv("WEB_SEARCH_MAX_RESULTS", "5")) CODE_INTERPRETER_TIMEOUT: int = int(os.getenv("CODE_INTERPRETER_TIMEOUT", "30")) @classmethod def validate(cls) -> bool: """ Validate the configuration Returns: True if configuration is valid, False otherwise """ if not cls.OPENROUTER_API_KEY: print("Error: OPENROUTER_API_KEY is not set") return False if not cls.OPENROUTER_API_KEY.startswith("sk-or-"): print("Warning: OPENROUTER_API_KEY should start with 'sk-or-'") return True @classmethod def display(cls): """Display current configuration (hiding sensitive data)""" print("=== GPT-5 Agent Configuration ===") print(f"API Base URL: {cls.OPENROUTER_BASE_URL}") print(f"Model: {cls.MODEL_NAME}") print(f"API Key: {'*' * 20 + cls.OPENROUTER_API_KEY[-4:] if cls.OPENROUTER_API_KEY else 'NOT SET'}") print(f"Temperature: {cls.DEFAULT_TEMPERATURE}") print(f"Max Tokens: {cls.DEFAULT_MAX_TOKENS}") print(f"Tool Choice: {cls.DEFAULT_TOOL_CHOICE}") print(f"Rate Limit: {cls.RATE_LIMIT_RPM} RPM") print("================================")
Convenience function for quick configuration check¶
def check_config() -> bool: """ Quick configuration check Returns: True if configuration is valid """ return Config.validate() ```
example_request.py¶
#!/usr/bin/env python3
"""
Example showing the exact OpenRouter GPT-5 request format matching the Go implementation
"""
import json
import requests
import os
from typing import Dict, Any
def make_gpt5_openrouter_request(
api_key: str,
system_prompt: str,
user_prompt: str,
reasoning_effort: str = "low"
) -> Dict[str, Any]:
"""
Make a GPT-5 request using the exact format from the Go implementation
This matches the GPT5OpenRouterRequest structure from the Go code
"""
# Build messages (matching Go implementation)
messages = [
{
"role": "system",
"content": system_prompt
},
{
"role": "user",
"content": user_prompt
}
]
# Build web search tool configuration (matching Go GPT5OpenRouterWebSearchTool)
web_search_tool = {
"type": "web_search",
"search_context_size": "medium",
"user_location": {
"type": "approximate",
"country": "US"
}
}
# Build request with OpenRouter-specific parameters (matching Go GPT5OpenRouterRequest)
request_body = {
"model": "openai/gpt-5.6-sol", # Default from Go code
"messages": messages,
"tools": [web_search_tool],
"tool_choice": "auto",
"parallel_tool_calls": True,
"reasoning": {
"effort": reasoning_effort,
"generate_summary": False
},
"background": False,
"stream": False # Can be set to True for streaming
}
print("="*60)
print("GPT-5 OpenRouter Request (matching Go implementation):")
print("="*60)
print(json.dumps(request_body, indent=2))
print("="*60)
# Set headers (matching Go implementation)
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {api_key}"
}
# Make the request
url = "https://openrouter.ai/api/v1/chat/completions"
try:
response = requests.post(
url,
headers=headers,
json=request_body,
timeout=600 # Match Go timeout
)
print(f"\nResponse Status: {response.status_code}")
if response.status_code == 200:
response_data = response.json()
# Log usage (matching Go logging)
if "usage" in response_data:
usage = response_data["usage"]
print(f"\nGPT-5 OpenRouter Usage:")
print(f" Input: {usage.get('input_tokens', 0)} tokens", end="")
if "input_tokens_details" in usage:
print(f" (cached: {usage['input_tokens_details'].get('cached_tokens', 0)})")
else:
print()
print(f" Output: {usage.get('output_tokens', 0)} tokens", end="")
if "output_tokens_details" in usage:
print(f" (reasoning: {usage['output_tokens_details'].get('reasoning_tokens', 0)})")
else:
print()
print(f" Total: {usage.get('total_tokens', 0)}")
return response_data
else:
print(f"\nError: {response.text}")
return {"error": response.text, "status_code": response.status_code}
except Exception as e:
print(f"\nException: {str(e)}")
return {"error": str(e)}
def demonstrate_streaming_response():
"""
Demonstrate how streaming would work (matching Go handleStreamingResponse)
"""
print("\n" + "="*60)
print("Streaming Response Handler (pseudo-code matching Go):")
print("="*60)
streaming_code = '''
def handle_streaming_response(response):
"""
Handle streaming responses from GPT-5 OpenRouter API
Matches Go handleStreamingResponse function
"""
content_builder = []
reasoning_builder = []
reasoning_token_count = 0
for line in response.iter_lines():
if not line:
continue
line_str = line.decode('utf-8')
if not line_str.startswith("data: "):
continue
data = line_str[6:] # Remove "data: " prefix
if data == "[DONE]":
break
try:
chunk = json.loads(data)
if "choices" in chunk and len(chunk["choices"]) > 0:
delta = chunk["choices"][0].get("delta", {})
# Check for reasoning content
if "reasoning_content" in delta:
reasoning = delta["reasoning_content"]
reasoning_builder.append(reasoning)
reasoning_token_count += 1
print(f"🧠 [GPT-5 THINKING] {reasoning}")
# Check for regular content
if "content" in delta:
content = delta["content"]
content_builder.append(content)
except json.JSONDecodeError:
continue
final_content = "".join(content_builder)
return final_content
'''
print(streaming_code)
def main():
"""
Main demonstration
"""
print("\n" + "="*60)
print(" GPT-5 OpenRouter Request Format Demo")
print(" Exact match with Go implementation")
print("="*60)
# Get API key from environment
api_key = os.getenv("OPENROUTER_API_KEY")
if not api_key:
print("\n❌ Error: OPENROUTER_API_KEY not found in environment")
print("Please set: export OPENROUTER_API_KEY=sk-or-v1-your-key-here")
return
# Example prompts
system_prompt = "You are a helpful AI assistant with web search capabilities."
user_prompt = "What are the latest developments in artificial intelligence?"
print("\n1. Making request with LOW reasoning effort:")
print("-"*60)
result_low = make_gpt5_openrouter_request(
api_key=api_key,
system_prompt=system_prompt,
user_prompt=user_prompt,
reasoning_effort="low"
)
if "choices" in result_low:
content = result_low["choices"][0]["message"]["content"]
print(f"\nResponse preview: {content[:200]}...")
print("\n2. Making request with HIGH reasoning effort:")
print("-"*60)
result_high = make_gpt5_openrouter_request(
api_key=api_key,
system_prompt=system_prompt,
user_prompt="Explain the implications of quantum computing on cryptography",
reasoning_effort="high"
)
if "choices" in result_high:
content = result_high["choices"][0]["message"]["content"]
print(f"\nResponse preview: {content[:200]}...")
# Show streaming handler
demonstrate_streaming_response()
print("\n" + "="*60)
print("Demo complete! This shows the exact request format from Go.")
print("="*60)
if __name__ == "__main__":
main()
main.py¶
"""
Main entry point for GPT-5 Native Tools Agent
Interactive CLI for using web_search and code_interpreter tools
"""
import sys
import json
import logging
from typing import Optional
from agent import GPT5NativeAgent, GPT5AgentChain
from config import Config
import argparse
# Set up logging
logging.basicConfig(
level=getattr(logging, Config.LOG_LEVEL),
format=Config.LOG_FORMAT
)
logger = logging.getLogger(__name__)
class InteractiveCLI:
"""Interactive command-line interface for GPT-5 Agent"""
def __init__(self):
"""Initialize the CLI"""
if not Config.validate():
raise ValueError("Invalid configuration. Please check your .env file")
self.agent = GPT5NativeAgent(
api_key=Config.OPENROUTER_API_KEY,
base_url=Config.OPENROUTER_BASE_URL,
model=Config.MODEL_NAME
)
self.commands = {
"/help": self.show_help,
"/clear": self.clear_history,
"/history": self.show_history,
"/tools": self.toggle_tools,
"/search": self.search_mode,
"/code": self.code_mode,
"/analyze": self.analyze_mode,
"/config": self.show_config,
"/reasoning": self.set_reasoning_effort,
"/exit": self.exit_cli,
"/quit": self.exit_cli,
}
self.use_tools = True
self.tool_choice = "auto"
self.reasoning_effort = "low" # Default reasoning effort
def show_help(self):
"""Display help information"""
help_text = """
Commands:
/help - Show this help message
/clear - Clear conversation history
/history - Show conversation history
/tools - Toggle tools on/off
/search - Enter web search mode
/code - Enter code interpreter mode
/analyze - Combined search + analysis mode
/config - Show current configuration
/reasoning - Set reasoning effort (low/medium/high)
/exit - Exit the application
Native Tools:
• web_search - Search the internet for real-time info
• code_interpreter - Execute Python code and analyze
Usage:
Simply type your request and the agent will use
appropriate tools automatically.
Examples:
"东盟 10 国首都之间,距离最近的两个首都是?给出你的详细分析推理过程。"
"搜索最近一年比特币的价格,计算收益率、最大回撤、年化波动等重要指标"
"""
print(help_text)
def clear_history(self):
"""Clear conversation history"""
self.agent.clear_history()
print("✅ Conversation history cleared")
def show_history(self):
"""Display conversation history"""
history = self.agent.get_history()
if not history:
print("📭 No conversation history")
return
print("\n" + "="*60)
print("CONVERSATION HISTORY")
print("="*60)
for i, msg in enumerate(history, 1):
role = msg["role"].upper()
content = msg["content"][:200] + "..." if len(msg["content"]) > 200 else msg["content"]
print(f"\n[{i}] {role}:\n{content}")
print("="*60)
def toggle_tools(self):
"""Toggle tool usage on/off"""
self.use_tools = not self.use_tools
status = "enabled" if self.use_tools else "disabled"
print(f"🔧 Tools {status}")
def search_mode(self):
"""Enter web search mode"""
print("\n🔍 Web Search Mode")
print("Enter your search query (or 'back' to return):")
query = input("> ").strip()
if query.lower() == "back":
return
request = f"Search the web for: {query}"
self._process_request(request, force_tools=True)
def code_mode(self):
"""Enter code interpreter mode"""
print("\n💻 Code Interpreter Mode")
print("Enter your code or computational request (or 'back' to return):")
request = input("> ").strip()
if request.lower() == "back":
return
enhanced_request = f"Use the code interpreter to: {request}"
self._process_request(enhanced_request, force_tools=True)
def analyze_mode(self):
"""Combined search and analysis mode"""
print("\n🔬 Search & Analyze Mode")
print("Enter topic to research and analyze (or 'back' to return):")
topic = input("> ").strip()
if topic.lower() == "back":
return
print("\nOptional: Enter Python code for analysis (press Enter to skip):")
code = input("> ").strip()
if code:
result = self.agent.search_and_analyze(topic, code)
else:
result = self.agent.search_and_analyze(topic)
self._display_result(result)
def show_config(self):
"""Display current configuration"""
Config.display()
print(f"\nCurrent Settings:")
print(f" Tools Enabled: {self.use_tools}")
print(f" Tool Choice: {self.tool_choice}")
print(f" Reasoning Effort: {self.reasoning_effort}")
def set_reasoning_effort(self):
"""Set the reasoning effort level"""
print("\n🧠 Set Reasoning Effort")
print("Options: low, medium, high")
print(f"Current: {self.reasoning_effort}")
effort = input("Enter new effort level: ").strip().lower()
if effort in ["low", "medium", "high"]:
self.reasoning_effort = effort
print(f"✅ Reasoning effort set to: {effort}")
else:
print(f"❌ Invalid effort level. Must be low, medium, or high")
def exit_cli(self):
"""Exit the application"""
print("\n👋 Goodbye!")
sys.exit(0)
def _process_request(self, request: str, force_tools: bool = False):
"""
Process a user request
Args:
request: User request
force_tools: Force tool usage regardless of settings
"""
use_tools = force_tools or self.use_tools
result = self.agent.process_request(
request,
use_tools=use_tools,
tool_choice=self.tool_choice if use_tools else "none",
temperature=Config.DEFAULT_TEMPERATURE,
max_tokens=Config.DEFAULT_MAX_TOKENS,
reasoning_effort=self.reasoning_effort
)
self._display_result(result)
def _display_result(self, result: dict):
"""
Display the result of a request
Args:
result: Result dictionary from agent
"""
print("\n" + "="*60)
if result["success"]:
# Display tool usage
if result["tool_calls"]:
print("🔧 Tools Used:")
for tool in result["tool_calls"]:
print(f" • {tool.tool_type.value}")
print()
# Display response
print("📝 Response:")
print("-"*60)
print(result["response"])
print("-"*60)
# Display token usage
if result.get("usage"):
usage = result["usage"]
total = usage.get("total_tokens", 0)
if total:
print(f"\n📊 Tokens used: {total}")
else:
print(f"❌ Error: {result.get('error', 'Unknown error')}")
print("="*60)
def run(self):
"""Run the interactive CLI"""
print("\n" + "="*60)
print(" 🤖 GPT-5 Native Tools Agent")
print(" Powered by OpenRouter API")
print("="*60)
self.show_help()
while True:
try:
print("\n💬 Enter your request (or /help for commands):")
user_input = input("> ").strip()
if not user_input:
continue
# Check for commands
if user_input.startswith("/"):
command = user_input.split()[0].lower()
if command in self.commands:
self.commands[command]()
else:
print(f"❌ Unknown command: {command}")
print("Type /help for available commands")
else:
# Process as regular request
self._process_request(user_input)
except KeyboardInterrupt:
print("\n\n⚠️ Interrupted. Type /exit to quit or continue chatting.")
except Exception as e:
logger.error(f"Error: {str(e)}")
print(f"❌ An error occurred: {str(e)}")
def _run_single(args):
"""执行单次请求(single / dry-run 模式),打印可读轨迹并按需保存结果。"""
# dry-run 只组装请求体、不联网,因此无需真实 API Key
api_key = Config.OPENROUTER_API_KEY or ("sk-or-DRYRUN-PLACEHOLDER" if args.dry_run else "")
agent = GPT5NativeAgent(
api_key=api_key,
base_url=Config.OPENROUTER_BASE_URL,
model=args.model or Config.MODEL_NAME
)
result = agent.process_request(
args.request,
use_tools=not args.no_tools,
temperature=Config.DEFAULT_TEMPERATURE,
max_tokens=Config.DEFAULT_MAX_TOKENS,
reasoning_effort=args.reasoning,
verbosity=args.verbosity,
dry_run=args.dry_run
)
# dry-run:打印将要发送给模型的完整请求体(原生工具定义 + 参数)
if result.get("dry_run"):
print("\n" + "=" * 60)
print("🧪 Dry-run:以下是发送给 GPT-5 的请求体(未联网)")
print("=" * 60)
print(f"Model: {result['model']}")
print(f"任务: {args.request}")
print("-" * 60)
print(json.dumps(result["request"], indent=2, ensure_ascii=False))
print("=" * 60)
elif result["success"]:
print("\n" + "=" * 60)
print("📝 Response:")
print("-" * 60)
print(result["response"])
print("-" * 60)
usage = result.get("usage") or {}
if usage:
print(
f"📊 Tokens - Input: {usage.get('input_tokens', 'N/A')}, "
f"Output: {usage.get('output_tokens', 'N/A')}, "
f"Reasoning: {usage.get('output_tokens_details', {}).get('reasoning_tokens', 0)}, "
f"Total: {usage.get('total_tokens', 'N/A')}"
)
print("=" * 60)
else:
print(f"❌ Error: {result.get('error')}")
# 按需将完整结果(含轨迹/请求体)保存为 JSON,便于复盘
if args.output:
with open(args.output, "w", encoding="utf-8") as f:
json.dump(result, f, indent=2, ensure_ascii=False)
print(f"💾 结果已保存到: {args.output}")
if not result["success"]:
sys.exit(1)
def main():
"""主入口:解析命令行参数并分派到交互 / 单次 / 测试模式。"""
parser = argparse.ArgumentParser(
description="GPT-5 原生工具 Agent —— 演示实验 1.3:网络搜索 + 代码解释器的原生 Deep Research 能力",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""示例:
python main.py # 交互模式(默认)
python main.py --mode single --request "东盟 10 国首都之间距离最近的两个首都是?"
python main.py --mode single --request "分析比特币近一月走势" --reasoning high --verbosity high
python main.py --mode single --request "..." --output result.json
python main.py --dry-run --request "..." # 离线查看请求体(原生工具定义),无需 API Key
python main.py --mode test --test basic # 运行指定测试用例
""",
)
parser.add_argument(
"--mode",
choices=["interactive", "single", "test"],
default="interactive",
help="运行模式:interactive 交互对话(默认)/ single 单次请求 / test 运行测试",
)
parser.add_argument(
"--request",
type=str,
help="single / dry-run 模式下的任务或查询内容",
)
parser.add_argument(
"--model",
type=str,
default=None,
help=f"覆盖模型名称(默认取配置 {Config.MODEL_NAME})",
)
parser.add_argument(
"--reasoning",
choices=["low", "medium", "high"],
default="low",
help="推理力度 Reasoning Effort(low/medium/high,默认 low)",
)
parser.add_argument(
"--verbosity",
choices=["low", "medium", "high"],
default=None,
help="输出详略程度 Verbosity(low/medium/high,默认跟随模型)",
)
parser.add_argument(
"--no-tools",
action="store_true",
help="禁用原生工具(web_search / code_interpreter)",
)
parser.add_argument(
"--output",
type=str,
default=None,
help="将完整结果(含轨迹 / 请求体)保存为 JSON 文件的路径",
)
parser.add_argument(
"--dry-run",
action="store_true",
help="离线组装并打印请求体(含原生工具定义),不调用 API、无需 API Key",
)
parser.add_argument(
"--test",
type=str,
help="test 模式下运行指定测试用例(basic/analysis/complex/code/reasoning/search_analyze/chain)",
)
args = parser.parse_args()
# dry-run:离线路径,跳过 API Key 校验
if args.dry_run:
if not args.request:
print("❌ --dry-run 需要配合 --request 使用")
sys.exit(1)
_run_single(args)
return
# 其余模式需要有效配置
if not Config.validate():
print("❌ 配置错误!")
print("请创建 .env 文件并填入 OPENROUTER_API_KEY")
print("\n示例 .env:")
print("OPENROUTER_API_KEY=sk-or-v1-your-key-here")
sys.exit(1)
if args.mode == "interactive":
cli = InteractiveCLI()
cli.run()
elif args.mode == "single":
if not args.request:
print("❌ single 模式需要 --request 参数")
sys.exit(1)
_run_single(args)
elif args.mode == "test":
from test_agent import TestGPT5Agent, run_single_test
if args.test:
run_single_test(args.test)
else:
tester = TestGPT5Agent()
tester.run_all_tests()
if __name__ == "__main__":
main()
quickstart.py¶
#!/usr/bin/env python3
"""
Quick Start Demo for GPT-5 Native Tools Agent
Demonstrates basic usage of web_search and code_interpreter tools
"""
import os
import sys
from agent import GPT5NativeAgent
from config import Config
def demo_web_search():
"""Demonstrate web search capability"""
print("\n" + "="*60)
print("DEMO: Web Search Tool")
print("="*60)
agent = GPT5NativeAgent(
api_key=Config.OPENROUTER_API_KEY,
base_url=Config.OPENROUTER_BASE_URL
)
result = agent.process_request(
"What are the latest developments in GPT-5 and its capabilities?",
use_tools=True,
reasoning_effort="low"
)
if result["success"]:
print("\n✅ Web Search Result:")
print(result["response"][:500] + "...")
if result["tool_calls"]:
print(f"\n🔧 Tools used: {len(result['tool_calls'])}")
else:
print(f"❌ Error: {result['error']}")
def demo_code_interpreter():
"""Demonstrate code generation and analysis capability"""
print("\n" + "="*60)
print("DEMO: Code Generation and Analysis")
print("="*60)
agent = GPT5NativeAgent(
api_key=Config.OPENROUTER_API_KEY,
base_url=Config.OPENROUTER_BASE_URL
)
result = agent.process_request(
"""Create Python code to:
1. Generate the first 20 Fibonacci numbers
2. Calculate their sum and average
3. Find the golden ratio approximation using consecutive pairs
4. Explain the mathematical significance""",
use_tools=True,
reasoning_effort="medium"
)
if result["success"]:
print("\n✅ Code and Analysis Result:")
print(result["response"][:500] + "...")
if result["tool_calls"]:
print(f"\n🔧 Tools used: {len(result['tool_calls'])}")
else:
print(f"❌ Error: {result['error']}")
def demo_combined_tools():
"""Demonstrate using both tools together"""
print("\n" + "="*60)
print("DEMO: Combined Web Search + Code Analysis")
print("="*60)
agent = GPT5NativeAgent(
api_key=Config.OPENROUTER_API_KEY,
base_url=Config.OPENROUTER_BASE_URL
)
result = agent.search_and_analyze(
topic="Current S&P 500 performance and major tech stocks",
analysis_code="""
# Analyze market data
import random
import statistics
# Simulate stock prices based on search results
stocks = {
'AAPL': [175 + random.uniform(-5, 5) for _ in range(10)],
'GOOGL': [140 + random.uniform(-3, 3) for _ in range(10)],
'MSFT': [380 + random.uniform(-8, 8) for _ in range(10)]
}
# Calculate metrics
for symbol, prices in stocks.items():
avg = statistics.mean(prices)
vol = statistics.stdev(prices)
trend = "↑" if prices[-1] > prices[0] else "↓"
print(f"{symbol}: Avg=${avg:.2f}, Volatility=${vol:.2f}, Trend={trend}")
"""
)
if result["success"]:
print("\n✅ Combined Analysis Result:")
print(result["response"][:500] + "...")
if result["tool_calls"]:
print(f"\n🔧 Tools used: {len(result['tool_calls'])}")
else:
print(f"❌ Error: {result['error']}")
def main():
"""Run all demos"""
print("\n" + "="*60)
print(" GPT-5 Native Tools Agent - Quick Start Demo")
print("="*60)
# Check configuration
if not Config.validate():
print("\n❌ Configuration Error!")
print("Please set up your .env file with OPENROUTER_API_KEY")
print("\nSteps:")
print("1. Copy env.example to .env")
print("2. Add your OpenRouter API key")
print("3. Get a key at: https://openrouter.ai/keys")
sys.exit(1)
print("\n✅ Configuration valid")
print(f"Using model: {Config.MODEL_NAME}")
# Ask user which demo to run
print("\nSelect demo to run:")
print("1. Web Search only")
print("2. Code Generation and Analysis")
print("3. Combined Tools")
print("4. All demos")
choice = input("\nEnter choice (1-4): ").strip()
if choice == "1":
demo_web_search()
elif choice == "2":
demo_code_interpreter()
elif choice == "3":
demo_combined_tools()
elif choice == "4":
demo_web_search()
demo_code_interpreter()
demo_combined_tools()
else:
print("Invalid choice. Running all demos...")
demo_web_search()
demo_code_interpreter()
demo_combined_tools()
print("\n" + "="*60)
print("Demo complete! 🎉")
print("\nNext steps:")
print("- Run 'python main.py' for interactive mode")
print("- Run 'python test_agent.py' for comprehensive tests")
print("- Check README.md for more examples")
print("="*60)
if __name__ == "__main__":
main()
test_agent.py¶
"""
Test cases for GPT-5 Native Tools Agent
These tests demonstrate the use of web_search tool with OpenRouter format
"""
import json
import logging
from typing import Dict, Any, List
from datetime import datetime
from agent import GPT5NativeAgent, GPT5AgentChain
from config import Config
# Set up logging
logging.basicConfig(
level=getattr(logging, Config.LOG_LEVEL),
format=Config.LOG_FORMAT
)
logger = logging.getLogger(__name__)
class TestGPT5Agent:
"""Test suite for GPT-5 Native Tools Agent"""
def __init__(self):
"""Initialize test suite"""
if not Config.validate():
raise ValueError("Invalid configuration. Please check your .env file")
self.agent = GPT5NativeAgent(
api_key=Config.OPENROUTER_API_KEY,
base_url=Config.OPENROUTER_BASE_URL,
model=Config.MODEL_NAME
)
self.results = []
def test_web_search_basic(self) -> Dict[str, Any]:
"""
Test Case 1: Basic web search
"""
print("\n" + "="*60)
print("TEST 1: Basic Web Search")
print("="*60)
request = """Search for the latest information about GPT-5 capabilities and features."""
result = self.agent.process_request(
request,
use_tools=True,
reasoning_effort="low"
)
self._print_result(result)
return result
def test_web_search_with_analysis(self) -> Dict[str, Any]:
"""
Test Case 2: Web search with analysis request
"""
print("\n" + "="*60)
print("TEST 2: Web Search with Analysis")
print("="*60)
request = """Search for current cryptocurrency market trends and Bitcoin price.
Then analyze the data to identify patterns and provide insights."""
result = self.agent.process_request(
request,
use_tools=True,
reasoning_effort="medium"
)
self._print_result(result)
return result
def test_complex_research(self) -> Dict[str, Any]:
"""
Test Case 3: Complex research task
"""
print("\n" + "="*60)
print("TEST 3: Complex Research Task")
print("="*60)
request = """Research the current state of renewable energy adoption globally.
Find statistics on solar, wind, and hydroelectric capacity.
Analyze growth trends and project future adoption rates.
Provide a comprehensive summary with data-driven insights."""
result = self.agent.process_request(
request,
use_tools=True,
reasoning_effort="high"
)
self._print_result(result)
return result
def test_search_and_code(self) -> Dict[str, Any]:
"""
Test Case 4: Search and code generation
"""
print("\n" + "="*60)
print("TEST 4: Search and Code Generation")
print("="*60)
request = """Search for the latest Python web frameworks in 2025.
Then create a simple comparison table and sample code for the top 3 frameworks."""
result = self.agent.process_request(
request,
use_tools=True,
reasoning_effort="medium"
)
self._print_result(result)
return result
def test_reasoning_efforts(self) -> List[Dict[str, Any]]:
"""
Test Case 5: Compare different reasoning efforts
"""
print("\n" + "="*60)
print("TEST 5: Reasoning Effort Comparison")
print("="*60)
request = "What are the implications of quantum computing on current encryption methods?"
results = []
for effort in ["low", "medium", "high"]:
print(f"\n--- Testing with {effort} reasoning effort ---")
result = self.agent.process_request(
request,
use_tools=True,
reasoning_effort=effort
)
self._print_result(result)
results.append({
"effort": effort,
"result": result
})
return results
def test_search_and_analyze_method(self) -> Dict[str, Any]:
"""
Test Case 6: Using the search_and_analyze convenience method
"""
print("\n" + "="*60)
print("TEST 6: Search and Analyze Method")
print("="*60)
analysis_code = """
# Analyze stock market data
import statistics
# Sample data processing
prices = [100, 102, 98, 105, 103, 107, 104]
returns = [(prices[i] - prices[i-1])/prices[i-1] * 100 for i in range(1, len(prices))]
avg_return = statistics.mean(returns)
volatility = statistics.stdev(returns)
print(f"Average Return: {avg_return:.2f}%")
print(f"Volatility: {volatility:.2f}%")
"""
result = self.agent.search_and_analyze(
topic="Current S&P 500 performance and market outlook for 2025",
analysis_code=analysis_code
)
self._print_result(result)
return result
def test_agent_chain(self) -> List[Dict[str, Any]]:
"""
Test Case 7: Chain multiple requests
"""
print("\n" + "="*60)
print("TEST 7: Agent Chain")
print("="*60)
chain = GPT5AgentChain(self.agent)
# Step 1: Research
chain.add_step(
"Search for information about the latest AI developments in 2025",
use_tools=True,
reasoning_effort="low"
)
# Step 2: Deep dive
chain.add_step(
"Based on the previous findings, search for more details about the most promising AI breakthrough",
use_tools=True,
reasoning_effort="medium"
)
# Step 3: Analysis
chain.add_step(
"Analyze the impact of these AI developments on various industries",
use_tools=True,
reasoning_effort="high"
)
results = chain.execute()
for i, step_result in enumerate(results, 1):
print(f"\n--- Chain Step {i} ---")
self._print_result(step_result["result"])
return results
def _print_result(self, result: Dict[str, Any]):
"""
Pretty print test result
Args:
result: Test result dictionary
"""
if result["success"]:
print(f"\n✅ Test Passed")
print(f"\nResponse Preview:")
print("-"*60)
response = result["response"]
if len(response) > 500:
print(response[:500] + "...")
else:
print(response)
print("-"*60)
if result.get("usage"):
usage = result["usage"]
print(f"\n📊 Token Usage:")
print(f" - Input: {usage.get('input_tokens', 'N/A')}")
print(f" - Output: {usage.get('output_tokens', 'N/A')}")
print(f" - Total: {usage.get('total_tokens', 'N/A')}")
if usage.get("input_tokens_details"):
print(f" - Cached: {usage['input_tokens_details'].get('cached_tokens', 0)}")
if usage.get("output_tokens_details"):
print(f" - Reasoning: {usage['output_tokens_details'].get('reasoning_tokens', 0)}")
else:
print(f"\n❌ Test Failed")
print(f"Error: {result.get('error', 'Unknown error')}")
def run_all_tests(self):
"""Run all test cases"""
print("\n" + "="*60)
print("RUNNING GPT-5 NATIVE TOOLS TEST SUITE")
print(f"Timestamp: {datetime.now().isoformat()}")
print(f"Model: {Config.MODEL_NAME}")
print("="*60)
test_methods = [
("Basic Web Search", self.test_web_search_basic),
("Web Search with Analysis", self.test_web_search_with_analysis),
("Complex Research", self.test_complex_research),
("Search and Code", self.test_search_and_code),
("Reasoning Efforts", self.test_reasoning_efforts),
("Search and Analyze Method", self.test_search_and_analyze_method),
("Agent Chain", self.test_agent_chain)
]
results_summary = []
for test_name, test_method in test_methods:
try:
print(f"\n🧪 Running: {test_name}")
result = test_method()
# Handle different result types
if isinstance(result, list):
# For tests that return multiple results
if all(isinstance(r, dict) and "result" in r for r in result):
success = all(r["result"]["success"] for r in result)
else:
success = all(r.get("success", False) for r in result if isinstance(r, dict))
else:
success = result.get("success", False)
results_summary.append({
"test": test_name,
"success": success,
"result": result
})
except Exception as e:
logger.error(f"Test {test_name} failed with exception: {str(e)}")
results_summary.append({
"test": test_name,
"success": False,
"error": str(e)
})
# Print summary
print("\n" + "="*60)
print("TEST SUMMARY")
print("="*60)
passed = sum(1 for r in results_summary if r["success"])
total = len(results_summary)
for result in results_summary:
status = "✅ PASS" if result["success"] else "❌ FAIL"
print(f"{result['test']}: {status}")
print(f"\nTotal: {passed}/{total} tests passed")
print("="*60)
return results_summary
def run_single_test(test_name: str = "basic"):
"""
Run a single test case
Args:
test_name: Name of test to run
"""
tester = TestGPT5Agent()
test_map = {
"basic": tester.test_web_search_basic,
"analysis": tester.test_web_search_with_analysis,
"complex": tester.test_complex_research,
"code": tester.test_search_and_code,
"reasoning": tester.test_reasoning_efforts,
"search_analyze": tester.test_search_and_analyze_method,
"chain": tester.test_agent_chain
}
if test_name in test_map:
test_map[test_name]()
else:
print(f"Unknown test: {test_name}")
print(f"Available tests: {', '.join(test_map.keys())}")
if __name__ == "__main__":
import sys
# Check configuration first
Config.display()
if not Config.validate():
print("\n❌ Configuration validation failed!")
print("Please set up your .env file with OPENROUTER_API_KEY")
sys.exit(1)
# Run tests
if len(sys.argv) > 1:
# Run specific test
run_single_test(sys.argv[1])
else:
# Run all tests
tester = TestGPT5Agent()
tester.run_all_tests()