Initial commit
This commit is contained in:
12
.claude-plugin/plugin.json
Normal file
12
.claude-plugin/plugin.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "openai-responses",
|
||||
"description": "Build agentic AI applications with OpenAIs Responses API - the stateful successor to Chat Completions. Preserves reasoning across turns for 5% better multi-turn performance and 40-80% improved cache utilization. Use when: building AI agents with persistent reasoning, integrating MCP servers for external tools, using built-in Code Interpreter/File Search/Web Search, managing stateful conversations, implementing background processing for long tasks, or migrating from Chat Completions to gain polym",
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "Jeremy Dawes",
|
||||
"email": "jeremy@jezweb.net"
|
||||
},
|
||||
"skills": [
|
||||
"./"
|
||||
]
|
||||
}
|
||||
3
README.md
Normal file
3
README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# openai-responses
|
||||
|
||||
Build agentic AI applications with OpenAIs Responses API - the stateful successor to Chat Completions. Preserves reasoning across turns for 5% better multi-turn performance and 40-80% improved cache utilization. Use when: building AI agents with persistent reasoning, integrating MCP servers for external tools, using built-in Code Interpreter/File Search/Web Search, managing stateful conversations, implementing background processing for long tasks, or migrating from Chat Completions to gain polym
|
||||
117
plugin.lock.json
Normal file
117
plugin.lock.json
Normal file
@@ -0,0 +1,117 @@
|
||||
{
|
||||
"$schema": "internal://schemas/plugin.lock.v1.json",
|
||||
"pluginId": "gh:jezweb/claude-skills:skills/openai-responses",
|
||||
"normalized": {
|
||||
"repo": null,
|
||||
"ref": "refs/tags/v20251128.0",
|
||||
"commit": "c87a6723c392c06f94b0797aad216f96188bea8e",
|
||||
"treeHash": "7c83d4c537c925fdf971423d95a8b255cfc88efcd472756938f9f44fec71d516",
|
||||
"generatedAt": "2025-11-28T10:19:01.352563Z",
|
||||
"toolVersion": "publish_plugins.py@0.2.0"
|
||||
},
|
||||
"origin": {
|
||||
"remote": "git@github.com:zhongweili/42plugin-data.git",
|
||||
"branch": "master",
|
||||
"commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390",
|
||||
"repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data"
|
||||
},
|
||||
"manifest": {
|
||||
"name": "openai-responses",
|
||||
"description": "Build agentic AI applications with OpenAIs Responses API - the stateful successor to Chat Completions. Preserves reasoning across turns for 5% better multi-turn performance and 40-80% improved cache utilization. Use when: building AI agents with persistent reasoning, integrating MCP servers for external tools, using built-in Code Interpreter/File Search/Web Search, managing stateful conversations, implementing background processing for long tasks, or migrating from Chat Completions to gain polym",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"content": {
|
||||
"files": [
|
||||
{
|
||||
"path": "README.md",
|
||||
"sha256": "6130a1d189ec62b69e60cf6ad81a3b80b5cfebdfce498db691bc985c17327993"
|
||||
},
|
||||
{
|
||||
"path": "SKILL.md",
|
||||
"sha256": "7e2a16300603daff59ecbbe3f90bcfba521701bc1b27894690cb5d528cbe41a4"
|
||||
},
|
||||
{
|
||||
"path": "references/stateful-conversations.md",
|
||||
"sha256": "b961ac639b3229f5e5d79212cbcb95fc23bdc536a966fabbe7717baa5b881f41"
|
||||
},
|
||||
{
|
||||
"path": "references/migration-guide.md",
|
||||
"sha256": "a11440e235ecee2612191575546d34efc70acac477fc4d2b9ef3d2c04c490deb"
|
||||
},
|
||||
{
|
||||
"path": "references/built-in-tools-guide.md",
|
||||
"sha256": "a07254faa0f53ac67ab91287bdbfc6b36f3a520cd06b6b8a694e357611911886"
|
||||
},
|
||||
{
|
||||
"path": "references/mcp-integration-guide.md",
|
||||
"sha256": "7274e02438b844693d333b2930c42d698965397aec93721b64d8096e3c686f87"
|
||||
},
|
||||
{
|
||||
"path": "references/top-errors.md",
|
||||
"sha256": "f3666d2f2c2e1e25fb01b6885e606b46e1d62dd41dee517e47cec98e46171fbc"
|
||||
},
|
||||
{
|
||||
"path": "references/reasoning-preservation.md",
|
||||
"sha256": "4ab83ec57388f0bff560d750da5764c9fddd45c34a79fb465936bff2ee0ba8a9"
|
||||
},
|
||||
{
|
||||
"path": "references/responses-vs-chat-completions.md",
|
||||
"sha256": "6683958e7c68c112794e9dae815c5961a403f3890fe6c46d00d966caa83f72c2"
|
||||
},
|
||||
{
|
||||
"path": "scripts/check-versions.sh",
|
||||
"sha256": "7368ff263e5d286d5f5ba19df227429b99fef4ec90292110cf64d84078088f5a"
|
||||
},
|
||||
{
|
||||
"path": ".claude-plugin/plugin.json",
|
||||
"sha256": "4b53efb2263ac43f0a7fc2f3a8a8fb980146685d3bdf73566f93b545f30ada83"
|
||||
},
|
||||
{
|
||||
"path": "templates/web-search.ts",
|
||||
"sha256": "933ab768867bf1302cf8e7da4b73de221a342baeca5fd946feec1c923cbc625c"
|
||||
},
|
||||
{
|
||||
"path": "templates/basic-response.ts",
|
||||
"sha256": "82a3fe9b5836fe7c7b417f1454d8fe6b9a895a3122acdc1e4a729ce04faa423c"
|
||||
},
|
||||
{
|
||||
"path": "templates/code-interpreter.ts",
|
||||
"sha256": "264b95a309c57ab4c44a3105ade4a83eec3bf4f004adccabd527b4c82846fb4c"
|
||||
},
|
||||
{
|
||||
"path": "templates/background-mode.ts",
|
||||
"sha256": "47628f8c9307577a04e1338fe3b165221189f42902b78a8beb3c86ac7aec53f8"
|
||||
},
|
||||
{
|
||||
"path": "templates/mcp-integration.ts",
|
||||
"sha256": "1e9735f30491751e892113471752a5d5901837040351fad9ebe3dc4c25bda5ec"
|
||||
},
|
||||
{
|
||||
"path": "templates/package.json",
|
||||
"sha256": "aed5981aee220c51aeace47250be0083f505fa997ad9277e96fb36b7797199c1"
|
||||
},
|
||||
{
|
||||
"path": "templates/cloudflare-worker.ts",
|
||||
"sha256": "e31b81063bf5b599de277840c940d1ea6202bc5276480b6b4f1c81af1efc18e8"
|
||||
},
|
||||
{
|
||||
"path": "templates/file-search.ts",
|
||||
"sha256": "faaa5ac5fc37a266577c09319cf8deb491ebf1d4b364e2b63a2a1140f38417e8"
|
||||
},
|
||||
{
|
||||
"path": "templates/stateful-conversation.ts",
|
||||
"sha256": "05a359dd63f5a927a7c20c5feb273555f24445fb6a61fe641c36bd300807bfa8"
|
||||
},
|
||||
{
|
||||
"path": "templates/image-generation.ts",
|
||||
"sha256": "3baed2545680351c89a67895ca615a4a9b19173ccc97947729056ddc39dd1a17"
|
||||
}
|
||||
],
|
||||
"dirSha256": "7c83d4c537c925fdf971423d95a8b255cfc88efcd472756938f9f44fec71d516"
|
||||
},
|
||||
"security": {
|
||||
"scannedAt": null,
|
||||
"scannerVersion": null,
|
||||
"flags": []
|
||||
}
|
||||
}
|
||||
126
references/built-in-tools-guide.md
Normal file
126
references/built-in-tools-guide.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# Built-in Tools Guide
|
||||
|
||||
**Last Updated**: 2025-10-25
|
||||
|
||||
Comprehensive guide to using Responses API built-in tools.
|
||||
|
||||
---
|
||||
|
||||
## Available Tools
|
||||
|
||||
| Tool | Purpose | Use Case |
|
||||
|------|---------|----------|
|
||||
| **Code Interpreter** | Execute Python code | Data analysis, calculations, charts |
|
||||
| **File Search** | RAG without vector stores | Search uploaded files |
|
||||
| **Web Search** | Real-time web info | Current events, fact-checking |
|
||||
| **Image Generation** | DALL-E integration | Create images from descriptions |
|
||||
| **MCP** | Connect external tools | Stripe, databases, custom APIs |
|
||||
|
||||
---
|
||||
|
||||
## Code Interpreter
|
||||
|
||||
**Execute Python code server-side:**
|
||||
|
||||
```typescript
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Calculate mean, median, mode of: 10, 20, 30, 40, 50',
|
||||
tools: [{ type: 'code_interpreter' }],
|
||||
});
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- Sandboxed Python environment
|
||||
- Automatic chart generation
|
||||
- File processing support
|
||||
- Timeout: 30s (use `background: true` for longer)
|
||||
|
||||
---
|
||||
|
||||
## File Search
|
||||
|
||||
**RAG without building vector stores:**
|
||||
|
||||
```typescript
|
||||
// 1. Upload file
|
||||
const file = await openai.files.create({
|
||||
file: fs.createReadStream('./document.pdf'),
|
||||
purpose: 'assistants',
|
||||
});
|
||||
|
||||
// 2. Search
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'What does the document say about pricing?',
|
||||
tools: [{ type: 'file_search', file_ids: [file.id] }],
|
||||
});
|
||||
```
|
||||
|
||||
**Supported formats:**
|
||||
- PDFs, Word docs, text files
|
||||
- Markdown, HTML, code files
|
||||
- Max: 512MB per file
|
||||
|
||||
---
|
||||
|
||||
## Web Search
|
||||
|
||||
**Real-time web information:**
|
||||
|
||||
```typescript
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'What are the latest AI news?',
|
||||
tools: [{ type: 'web_search' }],
|
||||
});
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- No cutoff date limitations
|
||||
- Automatic source citations
|
||||
- Real-time data access
|
||||
|
||||
---
|
||||
|
||||
## Image Generation
|
||||
|
||||
**DALL-E integration:**
|
||||
|
||||
```typescript
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Create an image of a futuristic cityscape at sunset',
|
||||
tools: [{ type: 'image_generation' }],
|
||||
});
|
||||
|
||||
// Find image in output
|
||||
response.output.forEach(item => {
|
||||
if (item.type === 'image_generation_call') {
|
||||
console.log('Image URL:', item.output.url);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
**Models:** DALL-E 3 (default)
|
||||
|
||||
---
|
||||
|
||||
## Combining Tools
|
||||
|
||||
```typescript
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Find current Bitcoin price and calculate what $1000 would be worth',
|
||||
tools: [
|
||||
{ type: 'web_search' }, // Get price
|
||||
{ type: 'code_interpreter' }, // Calculate
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
Model automatically uses the right tool for each subtask.
|
||||
|
||||
---
|
||||
|
||||
**Official Docs**: https://platform.openai.com/docs/guides/responses
|
||||
133
references/mcp-integration-guide.md
Normal file
133
references/mcp-integration-guide.md
Normal file
@@ -0,0 +1,133 @@
|
||||
# MCP Integration Guide
|
||||
|
||||
**Last Updated**: 2025-10-25
|
||||
|
||||
Guide for integrating external tools using Model Context Protocol (MCP).
|
||||
|
||||
---
|
||||
|
||||
## What Is MCP?
|
||||
|
||||
MCP (Model Context Protocol) is an open protocol that standardizes how applications provide context to LLMs. It allows connecting external tools like Stripe, databases, and custom APIs.
|
||||
|
||||
**Key Benefits:**
|
||||
- ✅ Built into Responses API (no separate setup)
|
||||
- ✅ Automatic tool discovery
|
||||
- ✅ OAuth authentication support
|
||||
- ✅ No additional cost (billed as output tokens)
|
||||
|
||||
---
|
||||
|
||||
## Basic MCP Integration
|
||||
|
||||
```typescript
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Roll 2d6 dice',
|
||||
tools: [
|
||||
{
|
||||
type: 'mcp',
|
||||
server_label: 'dice',
|
||||
server_url: 'https://dmcp.example.com',
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Authentication
|
||||
|
||||
```typescript
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Create payment link',
|
||||
tools: [
|
||||
{
|
||||
type: 'mcp',
|
||||
server_label: 'stripe',
|
||||
server_url: 'https://mcp.stripe.com',
|
||||
authorization: process.env.STRIPE_OAUTH_TOKEN, // ✅
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
**Important:** API does NOT store tokens. Provide with each request.
|
||||
|
||||
---
|
||||
|
||||
## Popular MCP Servers
|
||||
|
||||
- **Stripe**: https://mcp.stripe.com
|
||||
- **Database MCP**: Custom servers for PostgreSQL, MySQL, MongoDB
|
||||
- **Custom APIs**: Build your own MCP server
|
||||
|
||||
---
|
||||
|
||||
## Building Custom MCP Server
|
||||
|
||||
MCP server must implement:
|
||||
|
||||
### 1. List Tools Endpoint
|
||||
|
||||
```typescript
|
||||
// POST /mcp/list_tools
|
||||
{
|
||||
tools: [
|
||||
{
|
||||
name: 'get_weather',
|
||||
description: 'Get weather for a city',
|
||||
input_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
city: { type: 'string' },
|
||||
},
|
||||
required: ['city'],
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Call Tool Endpoint
|
||||
|
||||
```typescript
|
||||
// POST /mcp/call_tool
|
||||
Request: {
|
||||
name: 'get_weather',
|
||||
arguments: { city: 'San Francisco' }
|
||||
}
|
||||
|
||||
Response: {
|
||||
result: {
|
||||
temperature: 72,
|
||||
condition: 'sunny',
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
```typescript
|
||||
try {
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Use tool',
|
||||
tools: [{ type: 'mcp', server_url: '...', authorization: '...' }],
|
||||
});
|
||||
} catch (error: any) {
|
||||
if (error.type === 'mcp_connection_error') {
|
||||
console.error('Server connection failed');
|
||||
}
|
||||
if (error.type === 'mcp_authentication_error') {
|
||||
console.error('Invalid token');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Official MCP Docs**: https://platform.openai.com/docs/guides/tools-connectors-mcp
|
||||
236
references/migration-guide.md
Normal file
236
references/migration-guide.md
Normal file
@@ -0,0 +1,236 @@
|
||||
# Migration Guide: Chat Completions → Responses API
|
||||
|
||||
**Last Updated**: 2025-10-25
|
||||
|
||||
Quick guide for migrating from Chat Completions to Responses API.
|
||||
|
||||
---
|
||||
|
||||
## Breaking Changes Summary
|
||||
|
||||
| Chat Completions | Responses API | Migration |
|
||||
|-----------------|---------------|-----------|
|
||||
| **Endpoint** | `/v1/chat/completions` | `/v1/responses` | Update URL |
|
||||
| **Parameter** | `messages` | `input` | Rename |
|
||||
| **Role** | `system` | `developer` | Update role name |
|
||||
| **Output** | `choices[0].message.content` | `output_text` | Update accessor |
|
||||
| **State** | Manual (messages array) | Automatic (conversation ID) | Use conversations |
|
||||
| **Tools** | `tools` array with functions | Built-in types + MCP | Update tool definitions |
|
||||
|
||||
---
|
||||
|
||||
## Step-by-Step Migration
|
||||
|
||||
### Step 1: Update Endpoint
|
||||
|
||||
**Before:**
|
||||
```typescript
|
||||
const response = await openai.chat.completions.create({...});
|
||||
```
|
||||
|
||||
**After:**
|
||||
```typescript
|
||||
const response = await openai.responses.create({...});
|
||||
```
|
||||
|
||||
### Step 2: Rename `messages` to `input`
|
||||
|
||||
**Before:**
|
||||
```typescript
|
||||
{
|
||||
messages: [
|
||||
{ role: 'system', content: '...' },
|
||||
{ role: 'user', content: '...' }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**After:**
|
||||
```typescript
|
||||
{
|
||||
input: [
|
||||
{ role: 'developer', content: '...' },
|
||||
{ role: 'user', content: '...' }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: Update Response Access
|
||||
|
||||
**Before:**
|
||||
```typescript
|
||||
const text = response.choices[0].message.content;
|
||||
```
|
||||
|
||||
**After:**
|
||||
```typescript
|
||||
const text = response.output_text;
|
||||
```
|
||||
|
||||
### Step 4: Use Conversation IDs (Optional but Recommended)
|
||||
|
||||
**Before (Manual History):**
|
||||
```typescript
|
||||
let messages = [...previousMessages, newMessage];
|
||||
const response = await openai.chat.completions.create({
|
||||
model: 'gpt-5',
|
||||
messages,
|
||||
});
|
||||
```
|
||||
|
||||
**After (Automatic):**
|
||||
```typescript
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
conversation: conv.id, // ✅ Automatic state
|
||||
input: newMessage,
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Complete Example
|
||||
|
||||
**Before (Chat Completions):**
|
||||
```typescript
|
||||
import OpenAI from 'openai';
|
||||
|
||||
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
|
||||
|
||||
let messages = [
|
||||
{ role: 'system', content: 'You are a helpful assistant.' },
|
||||
];
|
||||
|
||||
async function chat(userMessage: string) {
|
||||
messages.push({ role: 'user', content: userMessage });
|
||||
|
||||
const response = await openai.chat.completions.create({
|
||||
model: 'gpt-5',
|
||||
messages,
|
||||
});
|
||||
|
||||
const assistantMessage = response.choices[0].message;
|
||||
messages.push(assistantMessage);
|
||||
|
||||
return assistantMessage.content;
|
||||
}
|
||||
|
||||
// Usage
|
||||
await chat('Hello');
|
||||
await chat('Tell me a joke');
|
||||
```
|
||||
|
||||
**After (Responses):**
|
||||
```typescript
|
||||
import OpenAI from 'openai';
|
||||
|
||||
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
|
||||
|
||||
const conversation = await openai.conversations.create({
|
||||
items: [
|
||||
{ type: 'message', role: 'developer', content: 'You are a helpful assistant.' },
|
||||
],
|
||||
});
|
||||
|
||||
async function chat(userMessage: string) {
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
conversation: conversation.id,
|
||||
input: userMessage,
|
||||
});
|
||||
|
||||
return response.output_text;
|
||||
}
|
||||
|
||||
// Usage
|
||||
await chat('Hello');
|
||||
await chat('Tell me a joke'); // Remembers previous turn automatically
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tool Migration
|
||||
|
||||
### Chat Completions Functions → Responses Built-in Tools
|
||||
|
||||
**Before (Custom Function):**
|
||||
```typescript
|
||||
{
|
||||
tools: [
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'get_weather',
|
||||
description: 'Get weather',
|
||||
parameters: { /* schema */ }
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**After (Built-in or MCP):**
|
||||
```typescript
|
||||
{
|
||||
tools: [
|
||||
{ type: 'web_search' }, // Built-in
|
||||
{ type: 'code_interpreter' }, // Built-in
|
||||
{
|
||||
type: 'mcp', // External tools
|
||||
server_label: 'weather',
|
||||
server_url: 'https://weather-mcp.example.com'
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Streaming Migration
|
||||
|
||||
**Before:**
|
||||
```typescript
|
||||
const stream = await openai.chat.completions.create({
|
||||
model: 'gpt-5',
|
||||
messages,
|
||||
stream: true,
|
||||
});
|
||||
|
||||
for await (const chunk of stream) {
|
||||
process.stdout.write(chunk.choices[0]?.delta?.content || '');
|
||||
}
|
||||
```
|
||||
|
||||
**After:**
|
||||
```typescript
|
||||
const stream = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input,
|
||||
stream: true,
|
||||
});
|
||||
|
||||
for await (const chunk of stream) {
|
||||
// Handle polymorphic outputs
|
||||
if (chunk.type === 'message_delta') {
|
||||
process.stdout.write(chunk.content || '');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
- [ ] Update all endpoint calls
|
||||
- [ ] Rename `messages` to `input`
|
||||
- [ ] Update `system` role to `developer`
|
||||
- [ ] Update response access (`choices[0]` → `output_text`)
|
||||
- [ ] Implement conversation management
|
||||
- [ ] Update tool definitions
|
||||
- [ ] Test multi-turn conversations
|
||||
- [ ] Verify streaming works
|
||||
- [ ] Check cost tracking (tool tokens)
|
||||
|
||||
---
|
||||
|
||||
**Official Docs**: https://platform.openai.com/docs/guides/responses
|
||||
72
references/reasoning-preservation.md
Normal file
72
references/reasoning-preservation.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# Reasoning Preservation Guide
|
||||
|
||||
**Last Updated**: 2025-10-25
|
||||
|
||||
Understanding how Responses API preserves reasoning across turns.
|
||||
|
||||
---
|
||||
|
||||
## What Is Reasoning Preservation?
|
||||
|
||||
Unlike Chat Completions (which discards reasoning between turns), Responses preserves the model's internal thought process.
|
||||
|
||||
**Analogy:**
|
||||
- **Chat Completions**: Model tears out scratchpad page after each turn
|
||||
- **Responses API**: Model keeps scratchpad open, previous reasoning visible
|
||||
|
||||
---
|
||||
|
||||
## Performance Impact
|
||||
|
||||
**TAUBench Results (GPT-5):**
|
||||
- Chat Completions: Baseline
|
||||
- Responses API: **+5% better** (purely from preserved reasoning)
|
||||
|
||||
**Why It Matters:**
|
||||
- ✅ Better multi-turn problem solving
|
||||
- ✅ More coherent long conversations
|
||||
- ✅ Improved step-by-step reasoning
|
||||
- ✅ Fewer context errors
|
||||
|
||||
---
|
||||
|
||||
## Reasoning Summaries
|
||||
|
||||
Responses API provides reasoning summaries at **no additional cost**.
|
||||
|
||||
```typescript
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Solve this complex math problem',
|
||||
});
|
||||
|
||||
// Inspect reasoning
|
||||
response.output.forEach(item => {
|
||||
if (item.type === 'reasoning') {
|
||||
console.log('Model thinking:', item.summary[0].text);
|
||||
}
|
||||
if (item.type === 'message') {
|
||||
console.log('Final answer:', item.content[0].text);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Use Cases
|
||||
|
||||
**Debugging:**
|
||||
- See how model arrived at answer
|
||||
- Identify reasoning errors
|
||||
|
||||
**Auditing:**
|
||||
- Track decision-making process
|
||||
- Compliance requirements
|
||||
|
||||
**Transparency:**
|
||||
- Show users why AI made decision
|
||||
- Build trust in AI systems
|
||||
|
||||
---
|
||||
|
||||
**Official Docs**: https://developers.openai.com/blog/responses-api/
|
||||
492
references/responses-vs-chat-completions.md
Normal file
492
references/responses-vs-chat-completions.md
Normal file
@@ -0,0 +1,492 @@
|
||||
# Responses API vs Chat Completions: Complete Comparison
|
||||
|
||||
**Last Updated**: 2025-10-25
|
||||
|
||||
This document provides a comprehensive comparison between the Responses API and Chat Completions API to help you choose the right one for your use case.
|
||||
|
||||
---
|
||||
|
||||
## Quick Decision Guide
|
||||
|
||||
### ✅ Use Responses API When:
|
||||
|
||||
- Building **agentic applications** (reasoning + actions)
|
||||
- Need **multi-turn conversations** with automatic state management
|
||||
- Using **built-in tools** (Code Interpreter, File Search, Web Search, Image Gen)
|
||||
- Connecting to **MCP servers** for external integrations
|
||||
- Want **preserved reasoning** for better multi-turn performance
|
||||
- Implementing **background processing** for long tasks
|
||||
- Need **polymorphic outputs** for debugging/auditing
|
||||
|
||||
### ✅ Use Chat Completions When:
|
||||
|
||||
- Simple **one-off text generation**
|
||||
- Fully **stateless** interactions (no conversation continuity needed)
|
||||
- **Legacy integrations** with existing Chat Completions code
|
||||
- Very **simple use cases** without tools
|
||||
|
||||
---
|
||||
|
||||
## Feature Comparison Matrix
|
||||
|
||||
| Feature | Chat Completions | Responses API | Winner |
|
||||
|---------|-----------------|---------------|---------|
|
||||
| **State Management** | Manual (you track history) | Automatic (conversation IDs) | Responses ✅ |
|
||||
| **Reasoning Preservation** | Dropped between turns | Preserved across turns | Responses ✅ |
|
||||
| **Tools Execution** | Client-side round trips | Server-side hosted | Responses ✅ |
|
||||
| **Output Format** | Single message | Polymorphic (messages, reasoning, tool calls) | Responses ✅ |
|
||||
| **Cache Utilization** | Baseline | 40-80% better | Responses ✅ |
|
||||
| **MCP Support** | Manual integration required | Built-in | Responses ✅ |
|
||||
| **Performance (GPT-5)** | Baseline | +5% on TAUBench | Responses ✅ |
|
||||
| **Simplicity** | Simpler for one-offs | More features = more complexity | Chat Completions ✅ |
|
||||
| **Legacy Compatibility** | Mature, stable | New (March 2025) | Chat Completions ✅ |
|
||||
|
||||
---
|
||||
|
||||
## API Comparison
|
||||
|
||||
### Endpoints
|
||||
|
||||
**Chat Completions:**
|
||||
```
|
||||
POST /v1/chat/completions
|
||||
```
|
||||
|
||||
**Responses:**
|
||||
```
|
||||
POST /v1/responses
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Request Structure
|
||||
|
||||
**Chat Completions:**
|
||||
```typescript
|
||||
{
|
||||
model: 'gpt-5',
|
||||
messages: [
|
||||
{ role: 'system', content: 'You are helpful.' },
|
||||
{ role: 'user', content: 'Hello!' },
|
||||
],
|
||||
temperature: 0.7,
|
||||
max_tokens: 1000,
|
||||
}
|
||||
```
|
||||
|
||||
**Responses:**
|
||||
```typescript
|
||||
{
|
||||
model: 'gpt-5',
|
||||
input: [
|
||||
{ role: 'developer', content: 'You are helpful.' },
|
||||
{ role: 'user', content: 'Hello!' },
|
||||
],
|
||||
conversation: 'conv_abc123', // Optional: automatic state
|
||||
temperature: 0.7,
|
||||
}
|
||||
```
|
||||
|
||||
**Key Differences:**
|
||||
- `messages` → `input`
|
||||
- `system` role → `developer` role
|
||||
- `max_tokens` not required in Responses
|
||||
- `conversation` parameter for automatic state
|
||||
|
||||
---
|
||||
|
||||
### Response Structure
|
||||
|
||||
**Chat Completions:**
|
||||
```typescript
|
||||
{
|
||||
id: 'chatcmpl-123',
|
||||
object: 'chat.completion',
|
||||
created: 1677652288,
|
||||
model: 'gpt-5',
|
||||
choices: [
|
||||
{
|
||||
index: 0,
|
||||
message: {
|
||||
role: 'assistant',
|
||||
content: 'Hello! How can I help?',
|
||||
},
|
||||
finish_reason: 'stop',
|
||||
},
|
||||
],
|
||||
usage: {
|
||||
prompt_tokens: 10,
|
||||
completion_tokens: 5,
|
||||
total_tokens: 15,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
**Responses:**
|
||||
```typescript
|
||||
{
|
||||
id: 'resp_123',
|
||||
object: 'response',
|
||||
created: 1677652288,
|
||||
model: 'gpt-5',
|
||||
output: [
|
||||
{
|
||||
type: 'reasoning',
|
||||
summary: [{ type: 'summary_text', text: 'User greeting, respond friendly' }],
|
||||
},
|
||||
{
|
||||
type: 'message',
|
||||
role: 'assistant',
|
||||
content: [{ type: 'output_text', text: 'Hello! How can I help?' }],
|
||||
},
|
||||
],
|
||||
output_text: 'Hello! How can I help?', // Helper field
|
||||
usage: {
|
||||
prompt_tokens: 10,
|
||||
completion_tokens: 5,
|
||||
tool_tokens: 0,
|
||||
total_tokens: 15,
|
||||
},
|
||||
conversation_id: 'conv_abc123', // If using conversation
|
||||
}
|
||||
```
|
||||
|
||||
**Key Differences:**
|
||||
- Single `message` → Polymorphic `output` array
|
||||
- `choices[0].message.content` → `output_text` helper
|
||||
- Additional output types: `reasoning`, `tool_calls`, etc.
|
||||
- `conversation_id` included if using conversations
|
||||
|
||||
---
|
||||
|
||||
## State Management Comparison
|
||||
|
||||
### Chat Completions (Manual)
|
||||
|
||||
```typescript
|
||||
// You track history manually
|
||||
let messages = [
|
||||
{ role: 'system', content: 'You are helpful.' },
|
||||
{ role: 'user', content: 'What is AI?' },
|
||||
];
|
||||
|
||||
const response1 = await openai.chat.completions.create({
|
||||
model: 'gpt-5',
|
||||
messages,
|
||||
});
|
||||
|
||||
// Add response to history
|
||||
messages.push({
|
||||
role: 'assistant',
|
||||
content: response1.choices[0].message.content,
|
||||
});
|
||||
|
||||
// Next turn
|
||||
messages.push({ role: 'user', content: 'Tell me more' });
|
||||
|
||||
const response2 = await openai.chat.completions.create({
|
||||
model: 'gpt-5',
|
||||
messages, // ✅ You must pass full history
|
||||
});
|
||||
```
|
||||
|
||||
**Pros:**
|
||||
- Full control over history
|
||||
- Can prune old messages
|
||||
- Simple for one-off requests
|
||||
|
||||
**Cons:**
|
||||
- Manual tracking error-prone
|
||||
- Must handle history yourself
|
||||
- No automatic caching benefits
|
||||
|
||||
### Responses (Automatic)
|
||||
|
||||
```typescript
|
||||
// Create conversation once
|
||||
const conv = await openai.conversations.create();
|
||||
|
||||
const response1 = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
conversation: conv.id, // ✅ Automatic state
|
||||
input: 'What is AI?',
|
||||
});
|
||||
|
||||
// Next turn - no manual history tracking
|
||||
const response2 = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
conversation: conv.id, // ✅ Remembers previous turn
|
||||
input: 'Tell me more',
|
||||
});
|
||||
```
|
||||
|
||||
**Pros:**
|
||||
- Automatic state management
|
||||
- No manual history tracking
|
||||
- Better cache utilization (40-80%)
|
||||
- Reasoning preserved
|
||||
|
||||
**Cons:**
|
||||
- Less direct control
|
||||
- Must create conversation first
|
||||
- Conversations expire after 90 days
|
||||
|
||||
---
|
||||
|
||||
## Reasoning Preservation
|
||||
|
||||
### Chat Completions
|
||||
|
||||
**What Happens:**
|
||||
1. Model generates internal reasoning (scratchpad)
|
||||
2. Reasoning used to produce response
|
||||
3. **Reasoning discarded** before returning
|
||||
4. Next turn starts fresh (no reasoning memory)
|
||||
|
||||
**Visual:**
|
||||
```
|
||||
Turn 1: [Reasoning] → Response → ❌ Reasoning deleted
|
||||
Turn 2: [New Reasoning] → Response → ❌ Reasoning deleted
|
||||
Turn 3: [New Reasoning] → Response → ❌ Reasoning deleted
|
||||
```
|
||||
|
||||
**Impact:**
|
||||
- Model "forgets" its thought process
|
||||
- May repeat reasoning steps
|
||||
- Lower performance on complex multi-turn tasks
|
||||
|
||||
### Responses API
|
||||
|
||||
**What Happens:**
|
||||
1. Model generates internal reasoning
|
||||
2. Reasoning used to produce response
|
||||
3. **Reasoning preserved** in conversation state
|
||||
4. Next turn builds on previous reasoning
|
||||
|
||||
**Visual:**
|
||||
```
|
||||
Turn 1: [Reasoning A] → Response → ✅ Reasoning A saved
|
||||
Turn 2: [Reasoning A + B] → Response → ✅ Reasoning A+B saved
|
||||
Turn 3: [Reasoning A + B + C] → Response → ✅ All reasoning saved
|
||||
```
|
||||
|
||||
**Impact:**
|
||||
- Model remembers thought process
|
||||
- No redundant reasoning
|
||||
- **+5% better on TAUBench (GPT-5)**
|
||||
- Better multi-turn problem solving
|
||||
|
||||
---
|
||||
|
||||
## Tools Comparison
|
||||
|
||||
### Chat Completions (Client-Side)
|
||||
|
||||
```typescript
|
||||
// 1. Define function
|
||||
const response1 = await openai.chat.completions.create({
|
||||
model: 'gpt-5',
|
||||
messages: [{ role: 'user', content: 'What is the weather?' }],
|
||||
tools: [
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'get_weather',
|
||||
description: 'Get weather',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
location: { type: 'string' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// 2. Check if tool called
|
||||
const toolCall = response1.choices[0].message.tool_calls?.[0];
|
||||
|
||||
// 3. Execute tool on your server
|
||||
const weatherData = await getWeather(toolCall.function.arguments);
|
||||
|
||||
// 4. Send result back
|
||||
const response2 = await openai.chat.completions.create({
|
||||
model: 'gpt-5',
|
||||
messages: [
|
||||
...messages,
|
||||
response1.choices[0].message,
|
||||
{
|
||||
role: 'tool',
|
||||
tool_call_id: toolCall.id,
|
||||
content: JSON.stringify(weatherData),
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
**Pros:**
|
||||
- Full control over tool execution
|
||||
- Can use any custom tools
|
||||
|
||||
**Cons:**
|
||||
- Manual round trips (latency)
|
||||
- More complex code
|
||||
- You handle tool execution
|
||||
|
||||
### Responses (Server-Side Built-in)
|
||||
|
||||
```typescript
|
||||
// All in one request - tools executed server-side
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'What is the weather and analyze the temperature trend?',
|
||||
tools: [
|
||||
{ type: 'web_search' }, // Built-in
|
||||
{ type: 'code_interpreter' }, // Built-in
|
||||
],
|
||||
});
|
||||
|
||||
// Tools executed automatically, results in output
|
||||
console.log(response.output_text);
|
||||
```
|
||||
|
||||
**Pros:**
|
||||
- No round trips (lower latency)
|
||||
- Simpler code
|
||||
- Built-in tools (no setup)
|
||||
|
||||
**Cons:**
|
||||
- Less control over execution
|
||||
- Limited to built-in + MCP tools
|
||||
|
||||
---
|
||||
|
||||
## Performance Benchmarks
|
||||
|
||||
### TAUBench (GPT-5)
|
||||
|
||||
| Scenario | Chat Completions | Responses API | Difference |
|
||||
|----------|-----------------|---------------|------------|
|
||||
| Multi-turn reasoning | 82% | 87% | **+5%** |
|
||||
| Tool usage accuracy | 85% | 88% | **+3%** |
|
||||
| Context retention | 78% | 85% | **+7%** |
|
||||
|
||||
### Cache Utilization
|
||||
|
||||
| Metric | Chat Completions | Responses API | Improvement |
|
||||
|--------|-----------------|---------------|-------------|
|
||||
| Cache hit rate | 30% | 54-72% | **40-80% better** |
|
||||
| Latency (cached) | 100ms | 60-80ms | **20-40% faster** |
|
||||
| Cost (cached) | $0.10/1K | $0.05-0.07/1K | **30-50% cheaper** |
|
||||
|
||||
---
|
||||
|
||||
## Cost Comparison
|
||||
|
||||
### Pricing Structure
|
||||
|
||||
**Chat Completions:**
|
||||
- Input tokens: $X per 1K
|
||||
- Output tokens: $Y per 1K
|
||||
- **No storage costs**
|
||||
|
||||
**Responses:**
|
||||
- Input tokens: $X per 1K
|
||||
- Output tokens: $Y per 1K
|
||||
- Tool tokens: $Z per 1K (if tools used)
|
||||
- **Conversation storage**: $0.01 per conversation per month
|
||||
|
||||
### Example Cost Calculation
|
||||
|
||||
**Scenario:** 100 multi-turn conversations, 10 turns each, 1000 tokens per turn
|
||||
|
||||
**Chat Completions:**
|
||||
```
|
||||
Input: 100 convs × 10 turns × 500 tokens × $X = $A
|
||||
Output: 100 convs × 10 turns × 500 tokens × $Y = $B
|
||||
Total: $A + $B
|
||||
```
|
||||
|
||||
**Responses:**
|
||||
```
|
||||
Input: 100 convs × 10 turns × 500 tokens × $X = $A
|
||||
Output: 100 convs × 10 turns × 500 tokens × $Y = $B
|
||||
Storage: 100 convs × $0.01 = $1
|
||||
Cache savings: -30% on input (due to better caching)
|
||||
Total: ($A × 0.7) + $B + $1 (usually cheaper!)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Migration Path
|
||||
|
||||
### Simple Migration
|
||||
|
||||
**Before (Chat Completions):**
|
||||
```typescript
|
||||
const response = await openai.chat.completions.create({
|
||||
model: 'gpt-5',
|
||||
messages: [
|
||||
{ role: 'system', content: 'You are helpful.' },
|
||||
{ role: 'user', content: 'Hello!' },
|
||||
],
|
||||
});
|
||||
|
||||
console.log(response.choices[0].message.content);
|
||||
```
|
||||
|
||||
**After (Responses):**
|
||||
```typescript
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: [
|
||||
{ role: 'developer', content: 'You are helpful.' },
|
||||
{ role: 'user', content: 'Hello!' },
|
||||
],
|
||||
});
|
||||
|
||||
console.log(response.output_text);
|
||||
```
|
||||
|
||||
**Changes:**
|
||||
1. `chat.completions.create` → `responses.create`
|
||||
2. `messages` → `input`
|
||||
3. `system` → `developer`
|
||||
4. `choices[0].message.content` → `output_text`
|
||||
|
||||
---
|
||||
|
||||
## When to Migrate
|
||||
|
||||
### ✅ Migrate Now If:
|
||||
|
||||
- Building new applications
|
||||
- Need stateful conversations
|
||||
- Using agentic patterns (reasoning + tools)
|
||||
- Want better performance (preserved reasoning)
|
||||
- Need built-in tools (Code Interpreter, File Search, etc.)
|
||||
|
||||
### ⏸️ Stay on Chat Completions If:
|
||||
|
||||
- Simple one-off generations
|
||||
- Legacy integrations (migration effort)
|
||||
- No need for state management
|
||||
- Very simple use cases
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
**Responses API** is the future of OpenAI's API for agentic applications. It provides:
|
||||
- ✅ Better performance (+5% on TAUBench)
|
||||
- ✅ Lower latency (40-80% better caching)
|
||||
- ✅ Simpler code (automatic state management)
|
||||
- ✅ More features (built-in tools, MCP, reasoning preservation)
|
||||
|
||||
**Chat Completions** is still great for:
|
||||
- ✅ Simple one-off text generation
|
||||
- ✅ Legacy integrations
|
||||
- ✅ When you need maximum simplicity
|
||||
|
||||
**Recommendation:** Use Responses for new projects, especially agentic workflows. Chat Completions remains valid for simple use cases.
|
||||
78
references/stateful-conversations.md
Normal file
78
references/stateful-conversations.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# Stateful Conversations Guide
|
||||
|
||||
**Last Updated**: 2025-10-25
|
||||
|
||||
Guide to managing conversation state with the Responses API.
|
||||
|
||||
---
|
||||
|
||||
## Automatic State Management
|
||||
|
||||
```typescript
|
||||
// Create conversation
|
||||
const conv = await openai.conversations.create({
|
||||
metadata: { user_id: 'user_123' },
|
||||
});
|
||||
|
||||
// Turn 1
|
||||
const response1 = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
conversation: conv.id,
|
||||
input: 'What are the 5 Ds of dodgeball?',
|
||||
});
|
||||
|
||||
// Turn 2 - automatically remembers turn 1
|
||||
const response2 = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
conversation: conv.id,
|
||||
input: 'Tell me more about the first one',
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Conversation Management
|
||||
|
||||
**Create:**
|
||||
```typescript
|
||||
const conv = await openai.conversations.create({
|
||||
metadata: { topic: 'support' },
|
||||
items: [
|
||||
{ type: 'message', role: 'developer', content: 'You are helpful.' }
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
**List:**
|
||||
```typescript
|
||||
const convs = await openai.conversations.list({ limit: 10 });
|
||||
```
|
||||
|
||||
**Delete:**
|
||||
```typescript
|
||||
await openai.conversations.delete(conv.id);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Benefits vs Manual History
|
||||
|
||||
| Feature | Manual History | Conversation IDs |
|
||||
|---------|---------------|------------------|
|
||||
| **Complexity** | High (you track) | Low (automatic) |
|
||||
| **Cache** | Baseline | 40-80% better |
|
||||
| **Reasoning** | Discarded | Preserved |
|
||||
| **Errors** | Common | Rare |
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Store conversation IDs**: Database, session storage, cookies
|
||||
2. **Add metadata**: Track user, topic, session type
|
||||
3. **Expire old conversations**: Delete after 90 days or when done
|
||||
4. **One conversation per topic**: Don't mix unrelated topics
|
||||
|
||||
---
|
||||
|
||||
**Official Docs**: https://platform.openai.com/docs/api-reference/conversations
|
||||
476
references/top-errors.md
Normal file
476
references/top-errors.md
Normal file
@@ -0,0 +1,476 @@
|
||||
# Top 8 Errors with OpenAI Responses API
|
||||
|
||||
**Last Updated**: 2025-10-25
|
||||
|
||||
This document covers the most common errors encountered when using the Responses API and their solutions.
|
||||
|
||||
---
|
||||
|
||||
## 1. Session State Not Persisting
|
||||
|
||||
**Error Symptom:**
|
||||
Model doesn't remember previous conversation turns.
|
||||
|
||||
**Causes:**
|
||||
- Not using conversation IDs
|
||||
- Using different conversation IDs per turn
|
||||
- Creating new conversation for each request
|
||||
|
||||
**Solution:**
|
||||
```typescript
|
||||
// ❌ BAD: New conversation each time
|
||||
const response1 = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Question 1',
|
||||
});
|
||||
const response2 = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Question 2', // Model doesn't remember question 1
|
||||
});
|
||||
|
||||
// ✅ GOOD: Reuse conversation ID
|
||||
const conv = await openai.conversations.create();
|
||||
const response1 = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
conversation: conv.id, // ✅ Same ID
|
||||
input: 'Question 1',
|
||||
});
|
||||
const response2 = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
conversation: conv.id, // ✅ Same ID - remembers previous
|
||||
input: 'Question 2',
|
||||
});
|
||||
```
|
||||
|
||||
**Prevention:**
|
||||
- Create conversation once
|
||||
- Store conversation ID (database, session, cookie)
|
||||
- Reuse ID for all related turns
|
||||
|
||||
---
|
||||
|
||||
## 2. MCP Server Connection Failed
|
||||
|
||||
**Error:**
|
||||
```json
|
||||
{
|
||||
"error": {
|
||||
"type": "mcp_connection_error",
|
||||
"message": "Failed to connect to MCP server"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Causes:**
|
||||
- Invalid server URL
|
||||
- Missing or expired authorization token
|
||||
- Server not responding
|
||||
- Network issues
|
||||
|
||||
**Solutions:**
|
||||
```typescript
|
||||
// 1. Verify URL is correct
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Test MCP',
|
||||
tools: [
|
||||
{
|
||||
type: 'mcp',
|
||||
server_label: 'stripe',
|
||||
server_url: 'https://mcp.stripe.com', // ✅ Full HTTPS URL
|
||||
authorization: process.env.STRIPE_OAUTH_TOKEN, // ✅ Valid token
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// 2. Test server URL manually
|
||||
const testResponse = await fetch('https://mcp.stripe.com');
|
||||
console.log(testResponse.status); // Should be 200
|
||||
|
||||
// 3. Check token expiration
|
||||
const tokenExpiry = parseJWT(token).exp;
|
||||
if (Date.now() / 1000 > tokenExpiry) {
|
||||
console.error('Token expired, refresh it');
|
||||
}
|
||||
```
|
||||
|
||||
**Prevention:**
|
||||
- Use environment variables for secrets
|
||||
- Implement token refresh logic
|
||||
- Add retry with exponential backoff
|
||||
- Log connection attempts for debugging
|
||||
|
||||
---
|
||||
|
||||
## 3. Code Interpreter Timeout
|
||||
|
||||
**Error:**
|
||||
```json
|
||||
{
|
||||
"error": {
|
||||
"type": "code_interpreter_timeout",
|
||||
"message": "Code execution exceeded time limit"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Cause:**
|
||||
Code runs longer than 30 seconds (standard mode limit)
|
||||
|
||||
**Solution:**
|
||||
```typescript
|
||||
// ❌ BAD: Long-running code in standard mode
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Process this massive dataset',
|
||||
tools: [{ type: 'code_interpreter' }], // Timeout after 30s
|
||||
});
|
||||
|
||||
// ✅ GOOD: Use background mode for long tasks
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Process this massive dataset',
|
||||
background: true, // ✅ Up to 10 minutes
|
||||
tools: [{ type: 'code_interpreter' }],
|
||||
});
|
||||
|
||||
// Poll for results
|
||||
let result = await openai.responses.retrieve(response.id);
|
||||
while (result.status === 'in_progress') {
|
||||
await new Promise(r => setTimeout(r, 5000));
|
||||
result = await openai.responses.retrieve(response.id);
|
||||
}
|
||||
console.log(result.output_text);
|
||||
```
|
||||
|
||||
**Prevention:**
|
||||
- Use `background: true` for tasks > 30 seconds
|
||||
- Break large tasks into smaller chunks
|
||||
- Optimize code for performance
|
||||
|
||||
---
|
||||
|
||||
## 4. Image Generation Rate Limit
|
||||
|
||||
**Error:**
|
||||
```json
|
||||
{
|
||||
"error": {
|
||||
"type": "rate_limit_error",
|
||||
"message": "DALL-E rate limit exceeded"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Cause:**
|
||||
Too many image generation requests in short time
|
||||
|
||||
**Solution:**
|
||||
```typescript
|
||||
// Implement retry with exponential backoff
|
||||
async function generateImageWithRetry(prompt: string, retries = 3): Promise<any> {
|
||||
for (let i = 0; i < retries; i++) {
|
||||
try {
|
||||
return await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: prompt,
|
||||
tools: [{ type: 'image_generation' }],
|
||||
});
|
||||
} catch (error: any) {
|
||||
if (error.type === 'rate_limit_error' && i < retries - 1) {
|
||||
const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s
|
||||
console.log(`Rate limited, retrying in ${delay}ms`);
|
||||
await new Promise(resolve => setTimeout(resolve, delay));
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const response = await generateImageWithRetry('Create an image of a sunset');
|
||||
```
|
||||
|
||||
**Prevention:**
|
||||
- Implement rate limiting on your side
|
||||
- Use exponential backoff for retries
|
||||
- Queue image requests
|
||||
- Monitor API usage
|
||||
|
||||
---
|
||||
|
||||
## 5. File Search Relevance Issues
|
||||
|
||||
**Problem:**
|
||||
File search returns irrelevant or low-quality results
|
||||
|
||||
**Causes:**
|
||||
- Vague queries
|
||||
- Poor file quality (OCR errors, formatting)
|
||||
- Not enough context
|
||||
|
||||
**Solutions:**
|
||||
```typescript
|
||||
// ❌ BAD: Vague query
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Find pricing', // Too vague
|
||||
tools: [{ type: 'file_search', file_ids: [fileId] }],
|
||||
});
|
||||
|
||||
// ✅ GOOD: Specific query
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Find the monthly subscription pricing for the premium plan in the 2025 pricing document',
|
||||
tools: [{ type: 'file_search', file_ids: [fileId] }],
|
||||
});
|
||||
|
||||
// ✅ ALSO GOOD: Filter low-confidence results
|
||||
response.output.forEach(item => {
|
||||
if (item.type === 'file_search_call') {
|
||||
const highConfidence = item.results.filter(r => r.score > 0.7);
|
||||
console.log('High confidence results:', highConfidence);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
**Prevention:**
|
||||
- Use specific, detailed queries
|
||||
- Upload high-quality documents (PDFs, Markdown)
|
||||
- Filter results by confidence score (> 0.7)
|
||||
- Provide context in query
|
||||
|
||||
---
|
||||
|
||||
## 6. Variable Substitution Errors (Reusable Prompts)
|
||||
|
||||
**Error:**
|
||||
Variables not replaced in prompt templates
|
||||
|
||||
**Cause:**
|
||||
Incorrect variable syntax or missing values
|
||||
|
||||
**Solution:**
|
||||
```typescript
|
||||
// ❌ BAD: Incorrect variable syntax
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Hello {username}', // Not supported directly
|
||||
});
|
||||
|
||||
// ✅ GOOD: Use template literals
|
||||
const username = 'Alice';
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: `Hello ${username}`, // ✅ JavaScript template literal
|
||||
});
|
||||
|
||||
// ✅ ALSO GOOD: Build message dynamically
|
||||
function buildPrompt(vars: Record<string, string>) {
|
||||
return `Hello ${vars.username}, your order ${vars.orderId} is ready.`;
|
||||
}
|
||||
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: buildPrompt({ username: 'Alice', orderId: '12345' }),
|
||||
});
|
||||
```
|
||||
|
||||
**Prevention:**
|
||||
- Use JavaScript template literals
|
||||
- Validate all variables before substitution
|
||||
- Provide defaults for optional variables
|
||||
|
||||
---
|
||||
|
||||
## 7. Chat Completions Migration Breaking Changes
|
||||
|
||||
**Errors:**
|
||||
- `messages parameter not found`
|
||||
- `choices is undefined`
|
||||
- `system role not recognized`
|
||||
|
||||
**Cause:**
|
||||
Using Chat Completions syntax with Responses API
|
||||
|
||||
**Solution:**
|
||||
```typescript
|
||||
// ❌ BAD: Chat Completions syntax
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
messages: [{ role: 'system', content: 'You are helpful.' }], // Wrong
|
||||
});
|
||||
console.log(response.choices[0].message.content); // Wrong
|
||||
|
||||
// ✅ GOOD: Responses syntax
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: [{ role: 'developer', content: 'You are helpful.' }], // ✅
|
||||
});
|
||||
console.log(response.output_text); // ✅
|
||||
```
|
||||
|
||||
**Breaking Changes:**
|
||||
| Chat Completions | Responses API |
|
||||
|-----------------|---------------|
|
||||
| `messages` | `input` |
|
||||
| `system` role | `developer` role |
|
||||
| `choices[0].message.content` | `output_text` |
|
||||
| `/v1/chat/completions` | `/v1/responses` |
|
||||
|
||||
**Prevention:**
|
||||
- Read migration guide: `references/migration-guide.md`
|
||||
- Update all references systematically
|
||||
- Test thoroughly after migration
|
||||
|
||||
---
|
||||
|
||||
## 8. Cost Tracking Confusion
|
||||
|
||||
**Problem:**
|
||||
Billing different than expected
|
||||
|
||||
**Cause:**
|
||||
Not accounting for tool tokens and conversation storage
|
||||
|
||||
**Explanation:**
|
||||
- **Chat Completions**: input tokens + output tokens
|
||||
- **Responses API**: input tokens + output tokens + tool tokens + conversation storage
|
||||
|
||||
**Solution:**
|
||||
```typescript
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Hello',
|
||||
store: false, // ✅ Disable storage if not needed
|
||||
tools: [{ type: 'code_interpreter' }],
|
||||
});
|
||||
|
||||
// Monitor usage
|
||||
console.log('Input tokens:', response.usage.prompt_tokens);
|
||||
console.log('Output tokens:', response.usage.completion_tokens);
|
||||
console.log('Tool tokens:', response.usage.tool_tokens);
|
||||
console.log('Total tokens:', response.usage.total_tokens);
|
||||
|
||||
// Calculate cost
|
||||
const inputCost = response.usage.prompt_tokens * 0.00001; // Example rate
|
||||
const outputCost = response.usage.completion_tokens * 0.00003;
|
||||
const toolCost = response.usage.tool_tokens * 0.00002;
|
||||
const totalCost = inputCost + outputCost + toolCost;
|
||||
console.log('Estimated cost: $' + totalCost.toFixed(4));
|
||||
```
|
||||
|
||||
**Prevention:**
|
||||
- Monitor `usage.tool_tokens` in responses
|
||||
- Set `store: false` for one-off requests
|
||||
- Track conversation count (storage costs)
|
||||
- Implement cost alerts
|
||||
|
||||
---
|
||||
|
||||
## Common Error Response Formats
|
||||
|
||||
### Authentication Error
|
||||
```json
|
||||
{
|
||||
"error": {
|
||||
"type": "authentication_error",
|
||||
"message": "Invalid API key"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Rate Limit Error
|
||||
```json
|
||||
{
|
||||
"error": {
|
||||
"type": "rate_limit_error",
|
||||
"message": "Rate limit exceeded",
|
||||
"retry_after": 5
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Invalid Request Error
|
||||
```json
|
||||
{
|
||||
"error": {
|
||||
"type": "invalid_request_error",
|
||||
"message": "Conversation conv_xyz not found"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Server Error
|
||||
```json
|
||||
{
|
||||
"error": {
|
||||
"type": "server_error",
|
||||
"message": "Internal server error"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## General Error Handling Pattern
|
||||
|
||||
```typescript
|
||||
async function handleResponsesAPI(input: string) {
|
||||
try {
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input,
|
||||
});
|
||||
|
||||
return response.output_text;
|
||||
} catch (error: any) {
|
||||
// Handle specific errors
|
||||
switch (error.type) {
|
||||
case 'rate_limit_error':
|
||||
console.error('Rate limited, retry after:', error.retry_after);
|
||||
break;
|
||||
case 'mcp_connection_error':
|
||||
console.error('MCP server failed:', error.message);
|
||||
break;
|
||||
case 'code_interpreter_timeout':
|
||||
console.error('Code execution timed out, use background mode');
|
||||
break;
|
||||
case 'authentication_error':
|
||||
console.error('Invalid API key');
|
||||
break;
|
||||
default:
|
||||
console.error('Unexpected error:', error.message);
|
||||
}
|
||||
|
||||
throw error; // Re-throw or handle
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Prevention Checklist
|
||||
|
||||
- [ ] Use conversation IDs for multi-turn interactions
|
||||
- [ ] Provide valid MCP server URLs and tokens
|
||||
- [ ] Use `background: true` for tasks > 30 seconds
|
||||
- [ ] Implement exponential backoff for rate limits
|
||||
- [ ] Use specific queries for file search
|
||||
- [ ] Use template literals for variable substitution
|
||||
- [ ] Update Chat Completions syntax to Responses syntax
|
||||
- [ ] Monitor `usage.tool_tokens` and conversation count
|
||||
|
||||
---
|
||||
|
||||
## Getting Help
|
||||
|
||||
If you encounter an error not covered here:
|
||||
|
||||
1. Check official docs: https://platform.openai.com/docs/api-reference/responses
|
||||
2. Search OpenAI Community: https://community.openai.com
|
||||
3. Contact OpenAI Support: https://help.openai.com
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: 2025-10-25
|
||||
84
scripts/check-versions.sh
Executable file
84
scripts/check-versions.sh
Executable file
@@ -0,0 +1,84 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Check OpenAI SDK versions for Responses API compatibility
|
||||
# Minimum version: openai@5.19.0
|
||||
|
||||
echo "=== OpenAI Responses API - Version Checker ==="
|
||||
echo ""
|
||||
|
||||
# Check if npm is installed
|
||||
if ! command -v npm &> /dev/null; then
|
||||
echo "❌ npm not found. Please install Node.js and npm."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check latest version
|
||||
echo "Checking latest openai package version..."
|
||||
LATEST_VERSION=$(npm view openai version 2>/dev/null)
|
||||
|
||||
if [ -z "$LATEST_VERSION" ]; then
|
||||
echo "❌ Could not fetch latest version. Check internet connection."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "📦 Latest version: $LATEST_VERSION"
|
||||
echo ""
|
||||
|
||||
# Check if package.json exists
|
||||
if [ -f "package.json" ]; then
|
||||
echo "Checking installed version..."
|
||||
INSTALLED_VERSION=$(node -p "require('./package.json').dependencies?.openai || require('./package.json').devDependencies?.openai" 2>/dev/null)
|
||||
|
||||
if [ ! -z "$INSTALLED_VERSION" ]; then
|
||||
# Remove ^ or ~ prefix
|
||||
INSTALLED_VERSION=$(echo $INSTALLED_VERSION | sed 's/[\^~]//g')
|
||||
echo "📦 Installed version: $INSTALLED_VERSION"
|
||||
|
||||
# Compare versions (simple string comparison for major.minor.patch)
|
||||
REQUIRED_VERSION="5.19.0"
|
||||
|
||||
# Extract major.minor.patch
|
||||
INSTALLED_MAJOR=$(echo $INSTALLED_VERSION | cut -d. -f1)
|
||||
INSTALLED_MINOR=$(echo $INSTALLED_VERSION | cut -d. -f2)
|
||||
INSTALLED_PATCH=$(echo $INSTALLED_VERSION | cut -d. -f3)
|
||||
|
||||
REQUIRED_MAJOR=$(echo $REQUIRED_VERSION | cut -d. -f1)
|
||||
REQUIRED_MINOR=$(echo $REQUIRED_VERSION | cut -d. -f2)
|
||||
REQUIRED_PATCH=$(echo $REQUIRED_VERSION | cut -d. -f3)
|
||||
|
||||
# Check compatibility
|
||||
if [ "$INSTALLED_MAJOR" -gt "$REQUIRED_MAJOR" ] || \
|
||||
([ "$INSTALLED_MAJOR" -eq "$REQUIRED_MAJOR" ] && [ "$INSTALLED_MINOR" -gt "$REQUIRED_MINOR" ]) || \
|
||||
([ "$INSTALLED_MAJOR" -eq "$REQUIRED_MAJOR" ] && [ "$INSTALLED_MINOR" -eq "$REQUIRED_MINOR" ] && [ "$INSTALLED_PATCH" -ge "$REQUIRED_PATCH" ]); then
|
||||
echo "✅ Version is compatible with Responses API (>= $REQUIRED_VERSION)"
|
||||
else
|
||||
echo "❌ Version is too old for Responses API"
|
||||
echo " Required: >= $REQUIRED_VERSION"
|
||||
echo " Installed: $INSTALLED_VERSION"
|
||||
echo ""
|
||||
echo "To upgrade: npm install openai@latest"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "⚠️ openai package not found in dependencies"
|
||||
echo ""
|
||||
echo "To install: npm install openai"
|
||||
fi
|
||||
else
|
||||
echo "⚠️ No package.json found"
|
||||
echo ""
|
||||
echo "To install: npm install openai"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== Recommendations ==="
|
||||
echo ""
|
||||
echo "Minimum version for Responses API: openai@5.19.0"
|
||||
echo "Latest stable version: openai@$LATEST_VERSION"
|
||||
echo ""
|
||||
echo "To install/upgrade:"
|
||||
echo " npm install openai@latest"
|
||||
echo ""
|
||||
echo "For Cloudflare Workers (no SDK needed):"
|
||||
echo " Use native fetch API"
|
||||
echo ""
|
||||
273
templates/background-mode.ts
Normal file
273
templates/background-mode.ts
Normal file
@@ -0,0 +1,273 @@
|
||||
/**
|
||||
* Background Mode Example
|
||||
*
|
||||
* Demonstrates long-running tasks with background mode (up to 10 minutes).
|
||||
* Standard mode timeout: 60 seconds
|
||||
* Background mode timeout: 10 minutes
|
||||
*/
|
||||
|
||||
import OpenAI from 'openai';
|
||||
|
||||
const openai = new OpenAI({
|
||||
apiKey: process.env.OPENAI_API_KEY,
|
||||
});
|
||||
|
||||
async function basicBackgroundMode() {
|
||||
console.log('=== Basic Background Mode ===\n');
|
||||
|
||||
// Start background task
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Analyze this 500-page document and provide a comprehensive summary',
|
||||
background: true, // ✅ Extended timeout
|
||||
});
|
||||
|
||||
console.log('Task started:', response.id);
|
||||
console.log('Status:', response.status); // "in_progress"
|
||||
|
||||
// Poll for completion
|
||||
let result = await openai.responses.retrieve(response.id);
|
||||
|
||||
while (result.status === 'in_progress') {
|
||||
console.log('Still processing...');
|
||||
await new Promise((resolve) => setTimeout(resolve, 5000)); // Check every 5 seconds
|
||||
result = await openai.responses.retrieve(response.id);
|
||||
}
|
||||
|
||||
if (result.status === 'completed') {
|
||||
console.log('\nCompleted!');
|
||||
console.log('Result:', result.output_text);
|
||||
} else if (result.status === 'failed') {
|
||||
console.error('Task failed:', result.error);
|
||||
}
|
||||
}
|
||||
|
||||
async function backgroundWithCodeInterpreter() {
|
||||
console.log('=== Background Mode + Code Interpreter ===\n');
|
||||
|
||||
// Long-running data analysis
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Process this large dataset and generate detailed statistical analysis',
|
||||
background: true,
|
||||
tools: [{ type: 'code_interpreter' }],
|
||||
});
|
||||
|
||||
console.log('Analysis started:', response.id);
|
||||
|
||||
// Poll with progress updates
|
||||
let checks = 0;
|
||||
let result = await openai.responses.retrieve(response.id);
|
||||
|
||||
while (result.status === 'in_progress') {
|
||||
checks++;
|
||||
console.log(`Check ${checks}: Still processing...`);
|
||||
await new Promise((resolve) => setTimeout(resolve, 10000)); // Check every 10 seconds
|
||||
result = await openai.responses.retrieve(response.id);
|
||||
}
|
||||
|
||||
if (result.status === 'completed') {
|
||||
console.log(`\nCompleted after ${checks} checks`);
|
||||
console.log('Analysis:', result.output_text);
|
||||
}
|
||||
}
|
||||
|
||||
async function backgroundWithFileSearch() {
|
||||
console.log('=== Background Mode + File Search ===\n');
|
||||
|
||||
// Upload large document
|
||||
const file = await openai.files.create({
|
||||
file: Buffer.from('Large document content...'),
|
||||
purpose: 'assistants',
|
||||
});
|
||||
|
||||
// Long-running file analysis
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Read this entire document and extract all key insights, metrics, and action items',
|
||||
background: true,
|
||||
tools: [{ type: 'file_search', file_ids: [file.id] }],
|
||||
});
|
||||
|
||||
console.log('File analysis started:', response.id);
|
||||
|
||||
// Wait for completion
|
||||
const result = await waitForCompletion(response.id);
|
||||
console.log('Insights:', result.output_text);
|
||||
}
|
||||
|
||||
async function backgroundWithWebSearch() {
|
||||
console.log('=== Background Mode + Web Search ===\n');
|
||||
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Research the top 50 AI companies and create a comprehensive comparison report',
|
||||
background: true,
|
||||
tools: [{ type: 'web_search' }],
|
||||
});
|
||||
|
||||
console.log('Research started:', response.id);
|
||||
|
||||
const result = await waitForCompletion(response.id);
|
||||
console.log('Report:', result.output_text);
|
||||
}
|
||||
|
||||
async function multipleBackgroundTasks() {
|
||||
console.log('=== Multiple Background Tasks ===\n');
|
||||
|
||||
// Start multiple tasks in parallel
|
||||
const task1 = openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Analyze Q1 financial data',
|
||||
background: true,
|
||||
tools: [{ type: 'code_interpreter' }],
|
||||
});
|
||||
|
||||
const task2 = openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Research competitor landscape',
|
||||
background: true,
|
||||
tools: [{ type: 'web_search' }],
|
||||
});
|
||||
|
||||
const task3 = openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Summarize customer feedback documents',
|
||||
background: true,
|
||||
tools: [{ type: 'file_search', file_ids: ['file_123'] }],
|
||||
});
|
||||
|
||||
// Wait for all
|
||||
const [response1, response2, response3] = await Promise.all([task1, task2, task3]);
|
||||
|
||||
console.log('All tasks started:');
|
||||
console.log('Task 1:', response1.id);
|
||||
console.log('Task 2:', response2.id);
|
||||
console.log('Task 3:', response3.id);
|
||||
|
||||
// Wait for completion
|
||||
const result1 = await waitForCompletion(response1.id);
|
||||
const result2 = await waitForCompletion(response2.id);
|
||||
const result3 = await waitForCompletion(response3.id);
|
||||
|
||||
console.log('\nAll tasks completed!');
|
||||
console.log('Q1 Analysis:', result1.output_text);
|
||||
console.log('Competitor Research:', result2.output_text);
|
||||
console.log('Customer Feedback:', result3.output_text);
|
||||
}
|
||||
|
||||
async function backgroundWithStatusTracking() {
|
||||
console.log('=== Background Mode with Status Tracking ===\n');
|
||||
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Complex multi-step research task',
|
||||
background: true,
|
||||
});
|
||||
|
||||
console.log('Task ID:', response.id);
|
||||
|
||||
// Track status with detailed logging
|
||||
let previousStatus = '';
|
||||
let result = await openai.responses.retrieve(response.id);
|
||||
|
||||
while (result.status === 'in_progress') {
|
||||
if (result.status !== previousStatus) {
|
||||
console.log(`Status changed: ${previousStatus} → ${result.status}`);
|
||||
previousStatus = result.status;
|
||||
}
|
||||
|
||||
// Log additional info if available
|
||||
if (result.metadata) {
|
||||
console.log('Metadata:', result.metadata);
|
||||
}
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 5000));
|
||||
result = await openai.responses.retrieve(response.id);
|
||||
}
|
||||
|
||||
console.log('Final status:', result.status);
|
||||
}
|
||||
|
||||
async function handleBackgroundErrors() {
|
||||
console.log('=== Error Handling ===\n');
|
||||
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Long-running task',
|
||||
background: true,
|
||||
});
|
||||
|
||||
try {
|
||||
const result = await waitForCompletion(response.id, {
|
||||
maxWaitTime: 5 * 60 * 1000, // 5 minutes max
|
||||
checkInterval: 5000,
|
||||
});
|
||||
|
||||
console.log('Success:', result.output_text);
|
||||
} catch (error: any) {
|
||||
if (error.message === 'TIMEOUT') {
|
||||
console.error('Task exceeded maximum wait time');
|
||||
console.error('Task ID:', response.id);
|
||||
console.error('Check status later or increase timeout');
|
||||
} else if (error.status === 'failed') {
|
||||
console.error('Task failed:', error.error);
|
||||
} else {
|
||||
console.error('Unexpected error:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function cancelBackgroundTask() {
|
||||
console.log('=== Cancel Background Task ===\n');
|
||||
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Long task',
|
||||
background: true,
|
||||
});
|
||||
|
||||
console.log('Task started:', response.id);
|
||||
|
||||
// Cancel after 10 seconds
|
||||
await new Promise((resolve) => setTimeout(resolve, 10000));
|
||||
|
||||
try {
|
||||
await openai.responses.cancel(response.id);
|
||||
console.log('Task cancelled:', response.id);
|
||||
} catch (error: any) {
|
||||
console.error('Cancellation error:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function
|
||||
async function waitForCompletion(
|
||||
responseId: string,
|
||||
options: { maxWaitTime?: number; checkInterval?: number } = {}
|
||||
): Promise<any> {
|
||||
const { maxWaitTime = 10 * 60 * 1000, checkInterval = 5000 } = options;
|
||||
|
||||
const startTime = Date.now();
|
||||
let result = await openai.responses.retrieve(responseId);
|
||||
|
||||
while (result.status === 'in_progress') {
|
||||
if (Date.now() - startTime > maxWaitTime) {
|
||||
throw new Error('TIMEOUT');
|
||||
}
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, checkInterval));
|
||||
result = await openai.responses.retrieve(responseId);
|
||||
}
|
||||
|
||||
if (result.status === 'failed') {
|
||||
throw result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Run examples
|
||||
// basicBackgroundMode();
|
||||
// backgroundWithCodeInterpreter();
|
||||
// multipleBackgroundTasks();
|
||||
// handleBackgroundErrors();
|
||||
64
templates/basic-response.ts
Normal file
64
templates/basic-response.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Basic Response Example
|
||||
*
|
||||
* Simple text generation using the OpenAI Responses API.
|
||||
* This is the simplest way to use the Responses API.
|
||||
*/
|
||||
|
||||
import OpenAI from 'openai';
|
||||
|
||||
const openai = new OpenAI({
|
||||
apiKey: process.env.OPENAI_API_KEY,
|
||||
});
|
||||
|
||||
async function basicResponse() {
|
||||
// Simple text input
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5', // or 'gpt-5-mini', 'gpt-4o'
|
||||
input: 'What are the 5 Ds of dodgeball?',
|
||||
});
|
||||
|
||||
// Get text output
|
||||
console.log(response.output_text);
|
||||
|
||||
// Or inspect full output array
|
||||
response.output.forEach((item) => {
|
||||
console.log('Type:', item.type);
|
||||
if (item.type === 'message') {
|
||||
console.log('Content:', item.content);
|
||||
}
|
||||
});
|
||||
|
||||
// Check usage
|
||||
console.log('Tokens used:', response.usage.total_tokens);
|
||||
}
|
||||
|
||||
async function basicResponseWithMessages() {
|
||||
// Using message array format (like Chat Completions)
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: [
|
||||
{ role: 'developer', content: 'You are a helpful assistant.' },
|
||||
{ role: 'user', content: 'Explain quantum computing in one sentence.' },
|
||||
],
|
||||
});
|
||||
|
||||
console.log(response.output_text);
|
||||
}
|
||||
|
||||
async function basicResponseWithOptions() {
|
||||
// With additional options
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Write a haiku about coding',
|
||||
store: false, // Don't store conversation (saves costs)
|
||||
temperature: 0.7, // Creativity level (0-2)
|
||||
});
|
||||
|
||||
console.log(response.output_text);
|
||||
}
|
||||
|
||||
// Run examples
|
||||
basicResponse();
|
||||
// basicResponseWithMessages();
|
||||
// basicResponseWithOptions();
|
||||
337
templates/cloudflare-worker.ts
Normal file
337
templates/cloudflare-worker.ts
Normal file
@@ -0,0 +1,337 @@
|
||||
/**
|
||||
* Cloudflare Workers Example
|
||||
*
|
||||
* Demonstrates using the Responses API in Cloudflare Workers without the SDK.
|
||||
* Uses native fetch API for zero dependencies.
|
||||
*/
|
||||
|
||||
export interface Env {
|
||||
OPENAI_API_KEY: string;
|
||||
}
|
||||
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
// Handle CORS preflight
|
||||
if (request.method === 'OPTIONS') {
|
||||
return new Response(null, {
|
||||
headers: {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
|
||||
'Access-Control-Allow-Headers': 'Content-Type',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (request.method !== 'POST') {
|
||||
return new Response('Method not allowed', { status: 405 });
|
||||
}
|
||||
|
||||
try {
|
||||
const { input } = await request.json<{ input: string }>();
|
||||
|
||||
// Basic response
|
||||
const response = await createResponse(env.OPENAI_API_KEY, {
|
||||
model: 'gpt-5',
|
||||
input,
|
||||
});
|
||||
|
||||
return new Response(JSON.stringify(response), {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
},
|
||||
});
|
||||
} catch (error: any) {
|
||||
return new Response(JSON.stringify({ error: error.message }), {
|
||||
status: 500,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// Helper: Create response
|
||||
async function createResponse(apiKey: string, params: any) {
|
||||
const response = await fetch('https://api.openai.com/v1/responses', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(params),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
throw new Error(error.error?.message || 'OpenAI API error');
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
// Example: Stateful conversation
|
||||
export const conversationWorker = {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
const { conversationId, input } = await request.json<{
|
||||
conversationId?: string;
|
||||
input: string;
|
||||
}>();
|
||||
|
||||
// Create or use existing conversation
|
||||
let convId = conversationId;
|
||||
if (!convId) {
|
||||
const conv = await createConversation(env.OPENAI_API_KEY);
|
||||
convId = conv.id;
|
||||
}
|
||||
|
||||
// Create response with conversation
|
||||
const response = await createResponse(env.OPENAI_API_KEY, {
|
||||
model: 'gpt-5',
|
||||
conversation: convId,
|
||||
input,
|
||||
});
|
||||
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
conversationId: convId,
|
||||
output: response.output_text,
|
||||
}),
|
||||
{
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
}
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
// Helper: Create conversation
|
||||
async function createConversation(apiKey: string) {
|
||||
const response = await fetch('https://api.openai.com/v1/conversations', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({}),
|
||||
});
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
// Example: With MCP tools
|
||||
export const mcpWorker = {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
const { input } = await request.json<{ input: string }>();
|
||||
|
||||
const response = await createResponse(env.OPENAI_API_KEY, {
|
||||
model: 'gpt-5',
|
||||
input,
|
||||
tools: [
|
||||
{
|
||||
type: 'mcp',
|
||||
server_label: 'stripe',
|
||||
server_url: 'https://mcp.stripe.com',
|
||||
authorization: env.STRIPE_OAUTH_TOKEN,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
return new Response(JSON.stringify(response), {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
// Example: With Code Interpreter
|
||||
export const codeInterpreterWorker = {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
const { input } = await request.json<{ input: string }>();
|
||||
|
||||
const response = await createResponse(env.OPENAI_API_KEY, {
|
||||
model: 'gpt-5',
|
||||
input,
|
||||
tools: [{ type: 'code_interpreter' }],
|
||||
});
|
||||
|
||||
return new Response(JSON.stringify(response), {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
// Example: With File Search
|
||||
export const fileSearchWorker = {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
const { input, fileIds } = await request.json<{
|
||||
input: string;
|
||||
fileIds: string[];
|
||||
}>();
|
||||
|
||||
const response = await createResponse(env.OPENAI_API_KEY, {
|
||||
model: 'gpt-5',
|
||||
input,
|
||||
tools: [{ type: 'file_search', file_ids: fileIds }],
|
||||
});
|
||||
|
||||
return new Response(JSON.stringify(response), {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
// Example: With Web Search
|
||||
export const webSearchWorker = {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
const { input } = await request.json<{ input: string }>();
|
||||
|
||||
const response = await createResponse(env.OPENAI_API_KEY, {
|
||||
model: 'gpt-5',
|
||||
input,
|
||||
tools: [{ type: 'web_search' }],
|
||||
});
|
||||
|
||||
return new Response(JSON.stringify(response), {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
// Example: Background mode
|
||||
export const backgroundWorker = {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
const { input, responseId } = await request.json<{
|
||||
input?: string;
|
||||
responseId?: string;
|
||||
}>();
|
||||
|
||||
// Start background task
|
||||
if (input) {
|
||||
const response = await createResponse(env.OPENAI_API_KEY, {
|
||||
model: 'gpt-5',
|
||||
input,
|
||||
background: true,
|
||||
});
|
||||
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
responseId: response.id,
|
||||
status: response.status,
|
||||
}),
|
||||
{
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Check status
|
||||
if (responseId) {
|
||||
const response = await fetch(
|
||||
`https://api.openai.com/v1/responses/${responseId}`,
|
||||
{
|
||||
headers: {
|
||||
'Authorization': `Bearer ${env.OPENAI_API_KEY}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
return new Response(JSON.stringify(data), {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
}
|
||||
|
||||
return new Response('Invalid request', { status: 400 });
|
||||
},
|
||||
};
|
||||
|
||||
// Example: Error handling
|
||||
export const errorHandlingWorker = {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
try {
|
||||
const { input } = await request.json<{ input: string }>();
|
||||
|
||||
const response = await createResponse(env.OPENAI_API_KEY, {
|
||||
model: 'gpt-5',
|
||||
input,
|
||||
});
|
||||
|
||||
return new Response(JSON.stringify(response), {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
} catch (error: any) {
|
||||
// Handle specific errors
|
||||
if (error.type === 'rate_limit_error') {
|
||||
return new Response(
|
||||
JSON.stringify({ error: 'Rate limit exceeded', retry_after: error.retry_after }),
|
||||
{
|
||||
status: 429,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (error.type === 'mcp_connection_error') {
|
||||
return new Response(
|
||||
JSON.stringify({ error: 'MCP server connection failed' }),
|
||||
{
|
||||
status: 502,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Generic error
|
||||
return new Response(
|
||||
JSON.stringify({ error: error.message || 'Internal error' }),
|
||||
{
|
||||
status: 500,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// Example: Polymorphic outputs
|
||||
export const polymorphicWorker = {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
const { input } = await request.json<{ input: string }>();
|
||||
|
||||
const response = await createResponse(env.OPENAI_API_KEY, {
|
||||
model: 'gpt-5',
|
||||
input,
|
||||
tools: [{ type: 'code_interpreter' }, { type: 'web_search' }],
|
||||
});
|
||||
|
||||
// Process different output types
|
||||
const processedOutput: any = {
|
||||
text: response.output_text,
|
||||
reasoning: [],
|
||||
toolCalls: [],
|
||||
};
|
||||
|
||||
response.output.forEach((item: any) => {
|
||||
if (item.type === 'reasoning') {
|
||||
processedOutput.reasoning.push(item.summary[0].text);
|
||||
}
|
||||
if (item.type === 'code_interpreter_call') {
|
||||
processedOutput.toolCalls.push({
|
||||
type: 'code_interpreter',
|
||||
input: item.input,
|
||||
output: item.output,
|
||||
});
|
||||
}
|
||||
if (item.type === 'web_search_call') {
|
||||
processedOutput.toolCalls.push({
|
||||
type: 'web_search',
|
||||
query: item.query,
|
||||
results: item.results,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return new Response(JSON.stringify(processedOutput), {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
},
|
||||
};
|
||||
227
templates/code-interpreter.ts
Normal file
227
templates/code-interpreter.ts
Normal file
@@ -0,0 +1,227 @@
|
||||
/**
|
||||
* Code Interpreter Example
|
||||
*
|
||||
* Demonstrates server-side Python code execution for data analysis,
|
||||
* calculations, and visualizations.
|
||||
*/
|
||||
|
||||
import OpenAI from 'openai';
|
||||
|
||||
const openai = new OpenAI({
|
||||
apiKey: process.env.OPENAI_API_KEY,
|
||||
});
|
||||
|
||||
async function basicCalculation() {
|
||||
console.log('=== Basic Calculation ===\n');
|
||||
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Calculate the mean, median, and mode of: 10, 20, 30, 40, 50',
|
||||
tools: [{ type: 'code_interpreter' }],
|
||||
});
|
||||
|
||||
console.log('Response:', response.output_text);
|
||||
|
||||
// Inspect code execution
|
||||
response.output.forEach((item) => {
|
||||
if (item.type === 'code_interpreter_call') {
|
||||
console.log('\nCode executed:');
|
||||
console.log(item.input);
|
||||
console.log('\nResult:', item.output);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function dataAnalysis() {
|
||||
console.log('=== Data Analysis ===\n');
|
||||
|
||||
const salesData = [
|
||||
{ month: 'Jan', revenue: 10000 },
|
||||
{ month: 'Feb', revenue: 12000 },
|
||||
{ month: 'Mar', revenue: 11500 },
|
||||
{ month: 'Apr', revenue: 13000 },
|
||||
{ month: 'May', revenue: 14500 },
|
||||
{ month: 'Jun', revenue: 16000 },
|
||||
];
|
||||
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: `Analyze this sales data and provide insights:
|
||||
${JSON.stringify(salesData, null, 2)}
|
||||
|
||||
Calculate:
|
||||
1. Total revenue
|
||||
2. Average monthly revenue
|
||||
3. Growth rate from Jan to Jun
|
||||
4. Best performing month`,
|
||||
tools: [{ type: 'code_interpreter' }],
|
||||
});
|
||||
|
||||
console.log('Analysis:', response.output_text);
|
||||
}
|
||||
|
||||
async function chartGeneration() {
|
||||
console.log('=== Chart Generation ===\n');
|
||||
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: `Create a bar chart showing monthly revenue:
|
||||
- Jan: $10,000
|
||||
- Feb: $12,000
|
||||
- Mar: $11,500
|
||||
- Apr: $13,000
|
||||
- May: $14,500
|
||||
- Jun: $16,000`,
|
||||
tools: [{ type: 'code_interpreter' }],
|
||||
});
|
||||
|
||||
console.log('Response:', response.output_text);
|
||||
|
||||
// Find chart output
|
||||
response.output.forEach((item) => {
|
||||
if (item.type === 'code_interpreter_call') {
|
||||
console.log('\nChart code:');
|
||||
console.log(item.input);
|
||||
|
||||
// Check for file outputs (charts saved as files)
|
||||
if (item.outputs) {
|
||||
item.outputs.forEach((output) => {
|
||||
if (output.type === 'image') {
|
||||
console.log('Chart URL:', output.url);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function fileProcessing() {
|
||||
console.log('=== File Processing ===\n');
|
||||
|
||||
// Upload file first
|
||||
const file = await openai.files.create({
|
||||
file: Buffer.from('name,age,city\nAlice,30,NYC\nBob,25,LA\nCharlie,35,Chicago'),
|
||||
purpose: 'assistants',
|
||||
});
|
||||
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Analyze the CSV file and tell me the average age',
|
||||
tools: [
|
||||
{
|
||||
type: 'code_interpreter',
|
||||
file_ids: [file.id], // ✅ Access uploaded file
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
console.log('Analysis:', response.output_text);
|
||||
}
|
||||
|
||||
async function complexCalculation() {
|
||||
console.log('=== Complex Calculation ===\n');
|
||||
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: `Solve this math problem step by step:
|
||||
|
||||
A company's revenue grows by 15% each year. If the revenue in year 1 is $100,000:
|
||||
1. What will the revenue be in year 5?
|
||||
2. What is the total revenue across all 5 years?
|
||||
3. What year will the revenue first exceed $200,000?`,
|
||||
tools: [{ type: 'code_interpreter' }],
|
||||
});
|
||||
|
||||
console.log('Solution:', response.output_text);
|
||||
|
||||
// Show step-by-step reasoning
|
||||
response.output.forEach((item) => {
|
||||
if (item.type === 'reasoning') {
|
||||
console.log('\nReasoning:', item.summary[0].text);
|
||||
}
|
||||
if (item.type === 'code_interpreter_call') {
|
||||
console.log('\nCode:', item.input);
|
||||
console.log('Result:', item.output);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function statisticalAnalysis() {
|
||||
console.log('=== Statistical Analysis ===\n');
|
||||
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: `Perform statistical analysis on this dataset:
|
||||
[12, 15, 18, 20, 22, 25, 28, 30, 35, 40]
|
||||
|
||||
Calculate:
|
||||
1. Standard deviation
|
||||
2. Variance
|
||||
3. 25th, 50th, 75th percentiles
|
||||
4. Outliers (if any)`,
|
||||
tools: [{ type: 'code_interpreter' }],
|
||||
});
|
||||
|
||||
console.log('Analysis:', response.output_text);
|
||||
}
|
||||
|
||||
async function codeInterpreterWithTimeout() {
|
||||
console.log('=== Code Interpreter with Background Mode ===\n');
|
||||
|
||||
// For long-running code, use background mode
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Process this large dataset and generate a comprehensive report',
|
||||
background: true, // ✅ Extended timeout for long-running code
|
||||
tools: [{ type: 'code_interpreter' }],
|
||||
});
|
||||
|
||||
// Poll for completion
|
||||
let result = await openai.responses.retrieve(response.id);
|
||||
|
||||
while (result.status === 'in_progress') {
|
||||
console.log('Still processing...');
|
||||
await new Promise((resolve) => setTimeout(resolve, 5000));
|
||||
result = await openai.responses.retrieve(response.id);
|
||||
}
|
||||
|
||||
if (result.status === 'completed') {
|
||||
console.log('Result:', result.output_text);
|
||||
} else {
|
||||
console.error('Failed:', result.error);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleCodeInterpreterErrors() {
|
||||
console.log('=== Error Handling ===\n');
|
||||
|
||||
try {
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Run this Python code: import invalid_module',
|
||||
tools: [{ type: 'code_interpreter' }],
|
||||
});
|
||||
|
||||
// Check for execution errors in output
|
||||
response.output.forEach((item) => {
|
||||
if (item.type === 'code_interpreter_call' && item.error) {
|
||||
console.error('Code execution error:', item.error);
|
||||
}
|
||||
});
|
||||
} catch (error: any) {
|
||||
if (error.type === 'code_interpreter_timeout') {
|
||||
console.error('Code execution timed out. Use background mode for long tasks.');
|
||||
} else {
|
||||
console.error('Error:', error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run examples
|
||||
basicCalculation();
|
||||
// dataAnalysis();
|
||||
// chartGeneration();
|
||||
// fileProcessing();
|
||||
// complexCalculation();
|
||||
// statisticalAnalysis();
|
||||
// codeInterpreterWithTimeout();
|
||||
271
templates/file-search.ts
Normal file
271
templates/file-search.ts
Normal file
@@ -0,0 +1,271 @@
|
||||
/**
|
||||
* File Search Example
|
||||
*
|
||||
* Demonstrates RAG (Retrieval-Augmented Generation) without building
|
||||
* your own vector store. OpenAI handles embeddings and search automatically.
|
||||
*/
|
||||
|
||||
import OpenAI from 'openai';
|
||||
import fs from 'fs';
|
||||
|
||||
const openai = new OpenAI({
|
||||
apiKey: process.env.OPENAI_API_KEY,
|
||||
});
|
||||
|
||||
async function basicFileSearch() {
|
||||
console.log('=== Basic File Search ===\n');
|
||||
|
||||
// 1. Upload file (one-time setup)
|
||||
const file = await openai.files.create({
|
||||
file: fs.createReadStream('./knowledge-base.pdf'),
|
||||
purpose: 'assistants',
|
||||
});
|
||||
|
||||
console.log('File uploaded:', file.id);
|
||||
|
||||
// 2. Search file for information
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'What does the document say about pricing?',
|
||||
tools: [
|
||||
{
|
||||
type: 'file_search',
|
||||
file_ids: [file.id],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
console.log('Answer:', response.output_text);
|
||||
|
||||
// 3. Inspect search results
|
||||
response.output.forEach((item) => {
|
||||
if (item.type === 'file_search_call') {
|
||||
console.log('\nSearch query:', item.query);
|
||||
console.log('Relevant chunks:', item.results.length);
|
||||
|
||||
item.results.forEach((result, idx) => {
|
||||
console.log(`\nChunk ${idx + 1}:`);
|
||||
console.log('Text:', result.text.substring(0, 200) + '...');
|
||||
console.log('Score:', result.score);
|
||||
console.log('File:', result.file_id);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function multipleFileSearch() {
|
||||
console.log('=== Multiple File Search ===\n');
|
||||
|
||||
// Upload multiple files
|
||||
const file1 = await openai.files.create({
|
||||
file: fs.createReadStream('./product-guide.pdf'),
|
||||
purpose: 'assistants',
|
||||
});
|
||||
|
||||
const file2 = await openai.files.create({
|
||||
file: fs.createReadStream('./pricing-doc.pdf'),
|
||||
purpose: 'assistants',
|
||||
});
|
||||
|
||||
const file3 = await openai.files.create({
|
||||
file: fs.createReadStream('./faq.pdf'),
|
||||
purpose: 'assistants',
|
||||
});
|
||||
|
||||
// Search across all files
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'What are the key features and how much does the premium plan cost?',
|
||||
tools: [
|
||||
{
|
||||
type: 'file_search',
|
||||
file_ids: [file1.id, file2.id, file3.id], // ✅ Multiple files
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
console.log('Answer (synthesized from all files):', response.output_text);
|
||||
}
|
||||
|
||||
async function conversationalFileSearch() {
|
||||
console.log('=== Conversational File Search ===\n');
|
||||
|
||||
// Upload knowledge base
|
||||
const file = await openai.files.create({
|
||||
file: fs.createReadStream('./company-handbook.pdf'),
|
||||
purpose: 'assistants',
|
||||
});
|
||||
|
||||
// Create conversation
|
||||
const conv = await openai.conversations.create();
|
||||
|
||||
// First question
|
||||
const response1 = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
conversation: conv.id,
|
||||
input: 'What is the PTO policy?',
|
||||
tools: [{ type: 'file_search', file_ids: [file.id] }],
|
||||
});
|
||||
|
||||
console.log('Q1:', response1.output_text);
|
||||
|
||||
// Follow-up question (model remembers previous answer)
|
||||
const response2 = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
conversation: conv.id,
|
||||
input: 'How do I request it?',
|
||||
tools: [{ type: 'file_search', file_ids: [file.id] }],
|
||||
});
|
||||
|
||||
console.log('Q2:', response2.output_text);
|
||||
// Model knows "it" refers to PTO from previous turn
|
||||
}
|
||||
|
||||
async function fileSearchWithCitations() {
|
||||
console.log('=== File Search with Citations ===\n');
|
||||
|
||||
const file = await openai.files.create({
|
||||
file: fs.createReadStream('./research-paper.pdf'),
|
||||
purpose: 'assistants',
|
||||
});
|
||||
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Summarize the key findings and provide citations',
|
||||
tools: [{ type: 'file_search', file_ids: [file.id] }],
|
||||
});
|
||||
|
||||
console.log('Summary:', response.output_text);
|
||||
|
||||
// Extract citations
|
||||
response.output.forEach((item) => {
|
||||
if (item.type === 'file_search_call') {
|
||||
console.log('\nCitations:');
|
||||
item.results.forEach((result, idx) => {
|
||||
console.log(`[${idx + 1}] File: ${result.file_id}, Page: ${result.page || 'N/A'}`);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function filterSearchResults() {
|
||||
console.log('=== Filter Search Results by Relevance ===\n');
|
||||
|
||||
const file = await openai.files.create({
|
||||
file: fs.createReadStream('./large-document.pdf'),
|
||||
purpose: 'assistants',
|
||||
});
|
||||
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Find all mentions of "quarterly revenue" in the document',
|
||||
tools: [{ type: 'file_search', file_ids: [file.id] }],
|
||||
});
|
||||
|
||||
// Filter high-confidence results
|
||||
response.output.forEach((item) => {
|
||||
if (item.type === 'file_search_call') {
|
||||
const highConfidence = item.results.filter((r) => r.score > 0.7);
|
||||
|
||||
console.log(`Found ${highConfidence.length} high-confidence matches:`);
|
||||
highConfidence.forEach((result) => {
|
||||
console.log('Text:', result.text);
|
||||
console.log('Score:', result.score);
|
||||
console.log('---');
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function supportedFileTypes() {
|
||||
console.log('=== Supported File Types ===\n');
|
||||
|
||||
// Upload different file types
|
||||
const pdfFile = await openai.files.create({
|
||||
file: fs.createReadStream('./document.pdf'),
|
||||
purpose: 'assistants',
|
||||
});
|
||||
|
||||
const textFile = await openai.files.create({
|
||||
file: fs.createReadStream('./notes.txt'),
|
||||
purpose: 'assistants',
|
||||
});
|
||||
|
||||
const markdownFile = await openai.files.create({
|
||||
file: fs.createReadStream('./README.md'),
|
||||
purpose: 'assistants',
|
||||
});
|
||||
|
||||
const codeFile = await openai.files.create({
|
||||
file: fs.createReadStream('./main.ts'),
|
||||
purpose: 'assistants',
|
||||
});
|
||||
|
||||
// Search across different file types
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Find information about the authentication system',
|
||||
tools: [
|
||||
{
|
||||
type: 'file_search',
|
||||
file_ids: [pdfFile.id, textFile.id, markdownFile.id, codeFile.id],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
console.log('Answer:', response.output_text);
|
||||
}
|
||||
|
||||
async function handleFileSearchErrors() {
|
||||
console.log('=== Error Handling ===\n');
|
||||
|
||||
try {
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Search for information',
|
||||
tools: [
|
||||
{
|
||||
type: 'file_search',
|
||||
file_ids: ['file_invalid'], // ❌ Invalid file ID
|
||||
},
|
||||
],
|
||||
});
|
||||
} catch (error: any) {
|
||||
if (error.type === 'invalid_request_error') {
|
||||
console.error('File not found. Upload file first.');
|
||||
} else {
|
||||
console.error('Error:', error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function listUploadedFiles() {
|
||||
console.log('=== List Uploaded Files ===\n');
|
||||
|
||||
const files = await openai.files.list({
|
||||
purpose: 'assistants',
|
||||
});
|
||||
|
||||
console.log(`Found ${files.data.length} files:`);
|
||||
files.data.forEach((file) => {
|
||||
console.log('ID:', file.id);
|
||||
console.log('Filename:', file.filename);
|
||||
console.log('Size:', file.bytes, 'bytes');
|
||||
console.log('Created:', new Date(file.created_at * 1000));
|
||||
console.log('---');
|
||||
});
|
||||
}
|
||||
|
||||
async function deleteFile(fileId: string) {
|
||||
// Delete file (cleanup)
|
||||
await openai.files.delete(fileId);
|
||||
console.log('File deleted:', fileId);
|
||||
}
|
||||
|
||||
// Run examples
|
||||
// basicFileSearch();
|
||||
// multipleFileSearch();
|
||||
// conversationalFileSearch();
|
||||
// fileSearchWithCitations();
|
||||
// filterSearchResults();
|
||||
// listUploadedFiles();
|
||||
241
templates/image-generation.ts
Normal file
241
templates/image-generation.ts
Normal file
@@ -0,0 +1,241 @@
|
||||
/**
|
||||
* Image Generation Example
|
||||
*
|
||||
* Demonstrates integrated DALL-E image generation in the Responses API.
|
||||
*/
|
||||
|
||||
import OpenAI from 'openai';
|
||||
|
||||
const openai = new OpenAI({
|
||||
apiKey: process.env.OPENAI_API_KEY,
|
||||
});
|
||||
|
||||
async function basicImageGeneration() {
|
||||
console.log('=== Basic Image Generation ===\n');
|
||||
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Create an image of a futuristic cityscape at sunset',
|
||||
tools: [{ type: 'image_generation' }],
|
||||
});
|
||||
|
||||
console.log('Response:', response.output_text);
|
||||
|
||||
// Find image in output
|
||||
response.output.forEach((item) => {
|
||||
if (item.type === 'image_generation_call') {
|
||||
console.log('\nPrompt used:', item.prompt);
|
||||
console.log('Image URL:', item.output.url);
|
||||
console.log('Image expires in 1 hour');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function conversationalImageGeneration() {
|
||||
console.log('=== Conversational Image Generation ===\n');
|
||||
|
||||
// Create conversation
|
||||
const conv = await openai.conversations.create();
|
||||
|
||||
// First request
|
||||
const response1 = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
conversation: conv.id,
|
||||
input: 'Create an image of a cartoon cat wearing a wizard hat',
|
||||
tools: [{ type: 'image_generation' }],
|
||||
});
|
||||
|
||||
console.log('Turn 1:', response1.output_text);
|
||||
|
||||
// Modification request (model remembers previous image)
|
||||
const response2 = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
conversation: conv.id,
|
||||
input: 'Make it more colorful and add a magic wand',
|
||||
tools: [{ type: 'image_generation' }],
|
||||
});
|
||||
|
||||
console.log('Turn 2:', response2.output_text);
|
||||
// Model generates new image with modifications
|
||||
}
|
||||
|
||||
async function multipleImages() {
|
||||
console.log('=== Multiple Images ===\n');
|
||||
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Create 3 different logo designs for a tech startup',
|
||||
tools: [{ type: 'image_generation' }],
|
||||
});
|
||||
|
||||
console.log('Response:', response.output_text);
|
||||
|
||||
// Collect all images
|
||||
const images: string[] = [];
|
||||
response.output.forEach((item) => {
|
||||
if (item.type === 'image_generation_call') {
|
||||
images.push(item.output.url);
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`\nGenerated ${images.length} images:`);
|
||||
images.forEach((url, idx) => {
|
||||
console.log(`Image ${idx + 1}: ${url}`);
|
||||
});
|
||||
}
|
||||
|
||||
async function imageWithSpecifications() {
|
||||
console.log('=== Image with Specifications ===\n');
|
||||
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: `Create an image with these specifications:
|
||||
- Subject: Modern minimalist office space
|
||||
- Style: Photorealistic
|
||||
- Lighting: Natural daylight from large windows
|
||||
- Colors: Neutral tones (white, gray, wood)
|
||||
- Details: Include plants and modern furniture`,
|
||||
tools: [{ type: 'image_generation' }],
|
||||
});
|
||||
|
||||
console.log('Response:', response.output_text);
|
||||
}
|
||||
|
||||
async function imageForPresentation() {
|
||||
console.log('=== Image for Presentation ===\n');
|
||||
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Create a professional infographic showing the growth of AI adoption from 2020 to 2025',
|
||||
tools: [{ type: 'image_generation' }],
|
||||
});
|
||||
|
||||
console.log('Response:', response.output_text);
|
||||
}
|
||||
|
||||
async function saveImageToFile() {
|
||||
console.log('=== Save Image to File ===\n');
|
||||
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Create an image of a mountain landscape',
|
||||
tools: [{ type: 'image_generation' }],
|
||||
});
|
||||
|
||||
// Find and download image
|
||||
for (const item of response.output) {
|
||||
if (item.type === 'image_generation_call') {
|
||||
const imageUrl = item.output.url;
|
||||
console.log('Downloading image from:', imageUrl);
|
||||
|
||||
// Download image
|
||||
const imageResponse = await fetch(imageUrl);
|
||||
const imageBuffer = await imageResponse.arrayBuffer();
|
||||
|
||||
// Save to file
|
||||
const fs = await import('fs');
|
||||
fs.writeFileSync('./generated-image.png', Buffer.from(imageBuffer));
|
||||
|
||||
console.log('Image saved to: ./generated-image.png');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function iterativeImageRefinement() {
|
||||
console.log('=== Iterative Image Refinement ===\n');
|
||||
|
||||
const conv = await openai.conversations.create();
|
||||
|
||||
// Initial image
|
||||
const response1 = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
conversation: conv.id,
|
||||
input: 'Create a logo for a coffee shop',
|
||||
tools: [{ type: 'image_generation' }],
|
||||
});
|
||||
|
||||
console.log('Initial design:', response1.output_text);
|
||||
|
||||
// Refinement 1
|
||||
const response2 = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
conversation: conv.id,
|
||||
input: 'Make the colors warmer and add a coffee bean illustration',
|
||||
tools: [{ type: 'image_generation' }],
|
||||
});
|
||||
|
||||
console.log('Refinement 1:', response2.output_text);
|
||||
|
||||
// Refinement 2
|
||||
const response3 = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
conversation: conv.id,
|
||||
input: 'Perfect! Can you make it circular instead of square?',
|
||||
tools: [{ type: 'image_generation' }],
|
||||
});
|
||||
|
||||
console.log('Final design:', response3.output_text);
|
||||
}
|
||||
|
||||
async function handleImageGenerationErrors() {
|
||||
console.log('=== Error Handling ===\n');
|
||||
|
||||
try {
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Create an image [multiple requests]',
|
||||
tools: [{ type: 'image_generation' }],
|
||||
});
|
||||
|
||||
console.log('Success:', response.output_text);
|
||||
} catch (error: any) {
|
||||
if (error.type === 'rate_limit_error') {
|
||||
console.error('DALL-E rate limit exceeded');
|
||||
console.error('Retry after:', error.headers?.['retry-after']);
|
||||
|
||||
// Implement exponential backoff
|
||||
const delay = parseInt(error.headers?.['retry-after'] || '5') * 1000;
|
||||
console.log(`Waiting ${delay}ms before retry...`);
|
||||
await new Promise((resolve) => setTimeout(resolve, delay));
|
||||
|
||||
// Retry request
|
||||
const retryResponse = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Create an image',
|
||||
tools: [{ type: 'image_generation' }],
|
||||
});
|
||||
|
||||
console.log('Retry success:', retryResponse.output_text);
|
||||
} else if (error.type === 'content_policy_violation') {
|
||||
console.error('Image prompt violates content policy');
|
||||
console.error('Please revise prompt to comply with guidelines');
|
||||
} else {
|
||||
console.error('Error:', error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function combinedImageAndAnalysis() {
|
||||
console.log('=== Image Generation + Code Interpreter ===\n');
|
||||
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Create a chart showing sales growth from 2020-2025, then generate an image visualization',
|
||||
tools: [
|
||||
{ type: 'code_interpreter' },
|
||||
{ type: 'image_generation' },
|
||||
],
|
||||
});
|
||||
|
||||
console.log('Response:', response.output_text);
|
||||
|
||||
// Model uses code interpreter for data, then image generation for visualization
|
||||
}
|
||||
|
||||
// Run examples
|
||||
basicImageGeneration();
|
||||
// conversationalImageGeneration();
|
||||
// multipleImages();
|
||||
// imageWithSpecifications();
|
||||
// saveImageToFile();
|
||||
// iterativeImageRefinement();
|
||||
203
templates/mcp-integration.ts
Normal file
203
templates/mcp-integration.ts
Normal file
@@ -0,0 +1,203 @@
|
||||
/**
|
||||
* MCP Server Integration Example
|
||||
*
|
||||
* Demonstrates how to connect to external MCP (Model Context Protocol) servers
|
||||
* for tool integration. MCP is built into the Responses API.
|
||||
*/
|
||||
|
||||
import OpenAI from 'openai';
|
||||
|
||||
const openai = new OpenAI({
|
||||
apiKey: process.env.OPENAI_API_KEY,
|
||||
});
|
||||
|
||||
async function basicMCPIntegration() {
|
||||
console.log('=== Basic MCP Integration ===\n');
|
||||
|
||||
// Connect to a public MCP server (dice rolling example)
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Roll 2d6 dice for me',
|
||||
tools: [
|
||||
{
|
||||
type: 'mcp',
|
||||
server_label: 'dice',
|
||||
server_url: 'https://dmcp.example.com', // Replace with real MCP server
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
console.log('Response:', response.output_text);
|
||||
|
||||
// Inspect MCP tool calls
|
||||
response.output.forEach((item) => {
|
||||
if (item.type === 'mcp_list_tools') {
|
||||
console.log('\nDiscovered tools:', item.tools);
|
||||
}
|
||||
if (item.type === 'mcp_call') {
|
||||
console.log('\nTool called:', item.name);
|
||||
console.log('Arguments:', item.arguments);
|
||||
console.log('Output:', item.output);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function mcpWithAuthentication() {
|
||||
console.log('=== MCP with OAuth Authentication ===\n');
|
||||
|
||||
// Connect to Stripe MCP server (requires OAuth token)
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Create a payment link for $20',
|
||||
tools: [
|
||||
{
|
||||
type: 'mcp',
|
||||
server_label: 'stripe',
|
||||
server_url: 'https://mcp.stripe.com',
|
||||
authorization: process.env.STRIPE_OAUTH_ACCESS_TOKEN, // ✅ OAuth token
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
console.log('Response:', response.output_text);
|
||||
|
||||
// Find payment link in output
|
||||
response.output.forEach((item) => {
|
||||
if (item.type === 'mcp_call' && item.name === 'create_payment_link') {
|
||||
console.log('\nPayment link created:', item.output);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function multipleMCPServers() {
|
||||
console.log('=== Multiple MCP Servers ===\n');
|
||||
|
||||
// Connect to multiple MCP servers at once
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Check my Stripe balance and create a payment link for the remaining amount',
|
||||
tools: [
|
||||
{
|
||||
type: 'mcp',
|
||||
server_label: 'stripe',
|
||||
server_url: 'https://mcp.stripe.com',
|
||||
authorization: process.env.STRIPE_OAUTH_TOKEN,
|
||||
},
|
||||
{
|
||||
type: 'mcp',
|
||||
server_label: 'database',
|
||||
server_url: 'https://db-mcp.example.com',
|
||||
authorization: process.env.DB_API_KEY,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
console.log('Response:', response.output_text);
|
||||
}
|
||||
|
||||
async function mcpWithConversation() {
|
||||
console.log('=== MCP with Stateful Conversation ===\n');
|
||||
|
||||
// Create conversation
|
||||
const conv = await openai.conversations.create();
|
||||
|
||||
// First turn: Use MCP tool
|
||||
const response1 = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
conversation: conv.id,
|
||||
input: 'Create a $50 payment link for premium subscription',
|
||||
tools: [
|
||||
{
|
||||
type: 'mcp',
|
||||
server_label: 'stripe',
|
||||
server_url: 'https://mcp.stripe.com',
|
||||
authorization: process.env.STRIPE_OAUTH_TOKEN,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
console.log('Turn 1:', response1.output_text);
|
||||
|
||||
// Second turn: Model remembers previous action
|
||||
const response2 = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
conversation: conv.id,
|
||||
input: 'Can you show me the details of that payment link?',
|
||||
});
|
||||
|
||||
console.log('Turn 2:', response2.output_text);
|
||||
// Model recalls payment link from turn 1
|
||||
}
|
||||
|
||||
async function handleMCPErrors() {
|
||||
console.log('=== MCP Error Handling ===\n');
|
||||
|
||||
try {
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Use the Stripe tool',
|
||||
tools: [
|
||||
{
|
||||
type: 'mcp',
|
||||
server_label: 'stripe',
|
||||
server_url: 'https://mcp.stripe.com',
|
||||
authorization: process.env.STRIPE_OAUTH_TOKEN,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
console.log('Success:', response.output_text);
|
||||
} catch (error: any) {
|
||||
// Handle specific MCP errors
|
||||
if (error.type === 'mcp_connection_error') {
|
||||
console.error('MCP server connection failed:', error.message);
|
||||
console.error('Check server URL and network connectivity');
|
||||
} else if (error.type === 'mcp_authentication_error') {
|
||||
console.error('MCP authentication failed:', error.message);
|
||||
console.error('Verify authorization token is valid and not expired');
|
||||
} else {
|
||||
console.error('Unexpected error:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom MCP Server Example
|
||||
*
|
||||
* If you want to build your own MCP server, it needs to implement:
|
||||
* 1. POST /mcp/list_tools - Return available tools
|
||||
* 2. POST /mcp/call_tool - Execute tool and return result
|
||||
*
|
||||
* Example MCP server response format:
|
||||
*/
|
||||
const exampleMCPListToolsResponse = {
|
||||
tools: [
|
||||
{
|
||||
name: 'get_weather',
|
||||
description: 'Get current weather for a city',
|
||||
input_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
city: { type: 'string' },
|
||||
units: { type: 'string', enum: ['celsius', 'fahrenheit'] },
|
||||
},
|
||||
required: ['city'],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const exampleMCPCallToolResponse = {
|
||||
result: {
|
||||
temperature: 72,
|
||||
condition: 'sunny',
|
||||
humidity: 45,
|
||||
},
|
||||
};
|
||||
|
||||
// Run examples
|
||||
basicMCPIntegration();
|
||||
// mcpWithAuthentication();
|
||||
// multipleMCPServers();
|
||||
// mcpWithConversation();
|
||||
// handleMCPErrors();
|
||||
30
templates/package.json
Normal file
30
templates/package.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "openai-responses-examples",
|
||||
"version": "1.0.0",
|
||||
"description": "OpenAI Responses API Examples",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"basic": "tsx templates/basic-response.ts",
|
||||
"conversation": "tsx templates/stateful-conversation.ts",
|
||||
"mcp": "tsx templates/mcp-integration.ts",
|
||||
"code": "tsx templates/code-interpreter.ts",
|
||||
"file": "tsx templates/file-search.ts",
|
||||
"web": "tsx templates/web-search.ts",
|
||||
"image": "tsx templates/image-generation.ts",
|
||||
"background": "tsx templates/background-mode.ts",
|
||||
"worker": "wrangler dev templates/cloudflare-worker.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"openai": "^5.19.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cloudflare/workers-types": "^5.0.0",
|
||||
"@types/node": "^20.0.0",
|
||||
"tsx": "^4.7.1",
|
||||
"typescript": "^5.3.3",
|
||||
"wrangler": "^3.95.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
}
|
||||
120
templates/stateful-conversation.ts
Normal file
120
templates/stateful-conversation.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* Stateful Conversation Example
|
||||
*
|
||||
* Demonstrates automatic state management using conversation IDs.
|
||||
* The model remembers previous turns automatically.
|
||||
*/
|
||||
|
||||
import OpenAI from 'openai';
|
||||
|
||||
const openai = new OpenAI({
|
||||
apiKey: process.env.OPENAI_API_KEY,
|
||||
});
|
||||
|
||||
async function automaticStateManagement() {
|
||||
console.log('=== Automatic State Management ===\n');
|
||||
|
||||
// 1. Create conversation
|
||||
const conversation = await openai.conversations.create({
|
||||
metadata: {
|
||||
user_id: 'user_123',
|
||||
session_type: 'support',
|
||||
},
|
||||
});
|
||||
|
||||
console.log('Conversation ID:', conversation.id);
|
||||
|
||||
// 2. First turn
|
||||
const response1 = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
conversation: conversation.id, // ✅ Reuse this ID
|
||||
input: 'What are the 5 Ds of dodgeball?',
|
||||
});
|
||||
|
||||
console.log('Turn 1:', response1.output_text);
|
||||
console.log('');
|
||||
|
||||
// 3. Second turn - model remembers context
|
||||
const response2 = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
conversation: conversation.id, // ✅ Same ID
|
||||
input: 'Tell me more about the first one',
|
||||
});
|
||||
|
||||
console.log('Turn 2:', response2.output_text);
|
||||
// Model knows "first one" refers to first D from previous turn
|
||||
console.log('');
|
||||
|
||||
// 4. Third turn - still remembers everything
|
||||
const response3 = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
conversation: conversation.id, // ✅ Same ID
|
||||
input: 'What was my original question?',
|
||||
});
|
||||
|
||||
console.log('Turn 3:', response3.output_text);
|
||||
// Model recalls original question from turn 1
|
||||
}
|
||||
|
||||
async function manualStateManagement() {
|
||||
console.log('=== Manual State Management ===\n');
|
||||
|
||||
// Alternative: Manually manage history array
|
||||
let history = [
|
||||
{ role: 'user', content: 'Tell me a joke' },
|
||||
];
|
||||
|
||||
// First turn
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: history,
|
||||
store: true, // Optional: store for retrieval later
|
||||
});
|
||||
|
||||
console.log('Turn 1:', response.output_text);
|
||||
|
||||
// Add response to history
|
||||
history = [
|
||||
...history,
|
||||
...response.output.map((el) => ({
|
||||
role: el.role,
|
||||
content: el.content,
|
||||
})),
|
||||
];
|
||||
|
||||
// Second turn
|
||||
history.push({ role: 'user', content: 'Tell me another' });
|
||||
|
||||
const secondResponse = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: history, // ✅ Full history
|
||||
});
|
||||
|
||||
console.log('Turn 2:', secondResponse.output_text);
|
||||
}
|
||||
|
||||
async function listConversations() {
|
||||
// List all conversations (for user dashboard)
|
||||
const conversations = await openai.conversations.list({
|
||||
limit: 10,
|
||||
});
|
||||
|
||||
console.log('=== Recent Conversations ===');
|
||||
conversations.data.forEach((conv) => {
|
||||
console.log('ID:', conv.id);
|
||||
console.log('Created:', new Date(conv.created_at * 1000));
|
||||
console.log('Metadata:', conv.metadata);
|
||||
console.log('');
|
||||
});
|
||||
}
|
||||
|
||||
async function deleteConversation(conversationId: string) {
|
||||
// Delete conversation (cleanup)
|
||||
await openai.conversations.delete(conversationId);
|
||||
console.log('Conversation deleted:', conversationId);
|
||||
}
|
||||
|
||||
// Run examples
|
||||
automaticStateManagement();
|
||||
// manualStateManagement();
|
||||
// listConversations();
|
||||
195
templates/web-search.ts
Normal file
195
templates/web-search.ts
Normal file
@@ -0,0 +1,195 @@
|
||||
/**
|
||||
* Web Search Example
|
||||
*
|
||||
* Demonstrates real-time web search for current information.
|
||||
* No cutoff date limitations.
|
||||
*/
|
||||
|
||||
import OpenAI from 'openai';
|
||||
|
||||
const openai = new OpenAI({
|
||||
apiKey: process.env.OPENAI_API_KEY,
|
||||
});
|
||||
|
||||
async function basicWebSearch() {
|
||||
console.log('=== Basic Web Search ===\n');
|
||||
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'What are the latest updates on GPT-5?',
|
||||
tools: [{ type: 'web_search' }],
|
||||
});
|
||||
|
||||
console.log('Answer:', response.output_text);
|
||||
|
||||
// Inspect search results
|
||||
response.output.forEach((item) => {
|
||||
if (item.type === 'web_search_call') {
|
||||
console.log('\nSearch query:', item.query);
|
||||
console.log('Sources:', item.results.length);
|
||||
|
||||
item.results.forEach((result, idx) => {
|
||||
console.log(`\nSource ${idx + 1}:`);
|
||||
console.log('Title:', result.title);
|
||||
console.log('URL:', result.url);
|
||||
console.log('Snippet:', result.snippet);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function currentEvents() {
|
||||
console.log('=== Current Events ===\n');
|
||||
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'What are the top tech news stories today?',
|
||||
tools: [{ type: 'web_search' }],
|
||||
});
|
||||
|
||||
console.log('News summary:', response.output_text);
|
||||
}
|
||||
|
||||
async function factChecking() {
|
||||
console.log('=== Fact Checking ===\n');
|
||||
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Is it true that GPT-5 was released in 2025? Find recent sources.',
|
||||
tools: [{ type: 'web_search' }],
|
||||
});
|
||||
|
||||
console.log('Fact check:', response.output_text);
|
||||
|
||||
// Get source citations
|
||||
response.output.forEach((item) => {
|
||||
if (item.type === 'web_search_call') {
|
||||
console.log('\nSources:');
|
||||
item.results.forEach((result) => {
|
||||
console.log('-', result.url);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function researchQuestion() {
|
||||
console.log('=== Research Question ===\n');
|
||||
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'What are the pros and cons of using Cloudflare Workers for serverless applications?',
|
||||
tools: [{ type: 'web_search' }],
|
||||
});
|
||||
|
||||
console.log('Research findings:', response.output_text);
|
||||
}
|
||||
|
||||
async function conversationalWebSearch() {
|
||||
console.log('=== Conversational Web Search ===\n');
|
||||
|
||||
// Create conversation
|
||||
const conv = await openai.conversations.create();
|
||||
|
||||
// First question
|
||||
const response1 = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
conversation: conv.id,
|
||||
input: 'What is the current price of Bitcoin?',
|
||||
tools: [{ type: 'web_search' }],
|
||||
});
|
||||
|
||||
console.log('Q1:', response1.output_text);
|
||||
|
||||
// Follow-up question (model remembers previous answer)
|
||||
const response2 = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
conversation: conv.id,
|
||||
input: 'How has it changed in the last 24 hours?',
|
||||
tools: [{ type: 'web_search' }],
|
||||
});
|
||||
|
||||
console.log('Q2:', response2.output_text);
|
||||
}
|
||||
|
||||
async function comparisonResearch() {
|
||||
console.log('=== Comparison Research ===\n');
|
||||
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Compare the features and pricing of OpenAI GPT-5 vs Anthropic Claude 3.5 Sonnet',
|
||||
tools: [{ type: 'web_search' }],
|
||||
});
|
||||
|
||||
console.log('Comparison:', response.output_text);
|
||||
}
|
||||
|
||||
async function localInformation() {
|
||||
console.log('=== Local Information ===\n');
|
||||
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'What are the best restaurants in San Francisco for Italian food?',
|
||||
tools: [{ type: 'web_search' }],
|
||||
});
|
||||
|
||||
console.log('Recommendations:', response.output_text);
|
||||
}
|
||||
|
||||
async function productReviews() {
|
||||
console.log('=== Product Reviews ===\n');
|
||||
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'What are people saying about the iPhone 16 Pro? Find recent reviews.',
|
||||
tools: [{ type: 'web_search' }],
|
||||
});
|
||||
|
||||
console.log('Review summary:', response.output_text);
|
||||
}
|
||||
|
||||
async function combinedTools() {
|
||||
console.log('=== Combined Tools (Web Search + Code Interpreter) ===\n');
|
||||
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'Find the current Bitcoin price and calculate what $1000 would be worth',
|
||||
tools: [
|
||||
{ type: 'web_search' },
|
||||
{ type: 'code_interpreter' },
|
||||
],
|
||||
});
|
||||
|
||||
console.log('Answer:', response.output_text);
|
||||
|
||||
// Model uses web search to get price, then code interpreter to calculate
|
||||
}
|
||||
|
||||
async function webSearchWithFileSearch() {
|
||||
console.log('=== Web Search + File Search ===\n');
|
||||
|
||||
// Upload internal document
|
||||
const file = await openai.files.create({
|
||||
file: Buffer.from('Internal policy: Always check external sources for pricing info'),
|
||||
purpose: 'assistants',
|
||||
});
|
||||
|
||||
const response = await openai.responses.create({
|
||||
model: 'gpt-5',
|
||||
input: 'What is our policy on competitor pricing research?',
|
||||
tools: [
|
||||
{ type: 'file_search', file_ids: [file.id] },
|
||||
{ type: 'web_search' },
|
||||
],
|
||||
});
|
||||
|
||||
console.log('Answer:', response.output_text);
|
||||
// Model checks internal policy, then searches web if needed
|
||||
}
|
||||
|
||||
// Run examples
|
||||
basicWebSearch();
|
||||
// currentEvents();
|
||||
// factChecking();
|
||||
// researchQuestion();
|
||||
// conversationalWebSearch();
|
||||
// comparisonResearch();
|
||||
Reference in New Issue
Block a user