Initial commit
This commit is contained in:
242
references/assistants-api-v2.md
Normal file
242
references/assistants-api-v2.md
Normal file
@@ -0,0 +1,242 @@
|
||||
# Assistants API v2 - Complete Overview
|
||||
|
||||
**Version**: v2 (v1 deprecated Dec 18, 2024)
|
||||
**Status**: Production (Deprecated H1 2026)
|
||||
**Replacement**: [Responses API](../../openai-responses/SKILL.md)
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
The Assistants API provides stateful conversational AI through four main objects:
|
||||
|
||||
```
|
||||
Assistant (configured AI entity)
|
||||
↓
|
||||
Thread (conversation container)
|
||||
↓
|
||||
Messages (user + assistant messages)
|
||||
↓
|
||||
Runs (execution on thread)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Core Objects
|
||||
|
||||
### 1. Assistants
|
||||
|
||||
Configured AI entities with:
|
||||
- **Instructions**: System prompt (max 256k characters)
|
||||
- **Model**: gpt-4o, gpt-5, etc.
|
||||
- **Tools**: code_interpreter, file_search, functions
|
||||
- **Tool Resources**: Vector stores, files
|
||||
- **Metadata**: Custom key-value pairs
|
||||
|
||||
**Lifecycle**: Create once, reuse many times.
|
||||
|
||||
### 2. Threads
|
||||
|
||||
Conversation containers:
|
||||
- **Persistent**: Store entire conversation history
|
||||
- **Capacity**: Up to 100,000 messages
|
||||
- **Reusable**: One thread per user for continuity
|
||||
- **Metadata**: Track ownership, session info
|
||||
|
||||
### 3. Messages
|
||||
|
||||
Individual conversation turns:
|
||||
- **Roles**: user, assistant
|
||||
- **Content**: Text, images, files
|
||||
- **Attachments**: Files with tool associations
|
||||
- **Metadata**: Custom tracking info
|
||||
|
||||
### 4. Runs
|
||||
|
||||
Asynchronous execution:
|
||||
- **States**: queued → in_progress → completed/failed/requires_action
|
||||
- **Streaming**: Real-time SSE events
|
||||
- **Tool Calls**: Automatic handling or requires_action
|
||||
- **Timeouts**: 10-minute max execution
|
||||
|
||||
---
|
||||
|
||||
## Workflow Patterns
|
||||
|
||||
### Basic Pattern
|
||||
|
||||
```typescript
|
||||
// 1. Create assistant (once)
|
||||
const assistant = await openai.beta.assistants.create({...});
|
||||
|
||||
// 2. Create thread (per conversation)
|
||||
const thread = await openai.beta.threads.create();
|
||||
|
||||
// 3. Add message
|
||||
await openai.beta.threads.messages.create(thread.id, {...});
|
||||
|
||||
// 4. Run
|
||||
const run = await openai.beta.threads.runs.create(thread.id, {
|
||||
assistant_id: assistant.id,
|
||||
});
|
||||
|
||||
// 5. Poll for completion
|
||||
while (run.status !== 'completed') {
|
||||
await sleep(1000);
|
||||
run = await openai.beta.threads.runs.retrieve(thread.id, run.id);
|
||||
}
|
||||
|
||||
// 6. Get response
|
||||
const messages = await openai.beta.threads.messages.list(thread.id);
|
||||
```
|
||||
|
||||
### Streaming Pattern
|
||||
|
||||
```typescript
|
||||
const stream = await openai.beta.threads.runs.stream(thread.id, {
|
||||
assistant_id: assistant.id,
|
||||
});
|
||||
|
||||
for await (const event of stream) {
|
||||
if (event.event === 'thread.message.delta') {
|
||||
process.stdout.write(event.data.delta.content?.[0]?.text?.value || '');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tools
|
||||
|
||||
### 1. Code Interpreter
|
||||
|
||||
- **Purpose**: Execute Python code
|
||||
- **Capabilities**: Data analysis, charts, file processing
|
||||
- **File Support**: CSV, JSON, images, etc.
|
||||
- **Outputs**: Text logs, image files
|
||||
|
||||
### 2. File Search
|
||||
|
||||
- **Purpose**: Semantic search over documents
|
||||
- **Capacity**: Up to 10,000 files per assistant
|
||||
- **Technology**: Vector + keyword search
|
||||
- **Pricing**: $0.10/GB/day (first 1GB free)
|
||||
|
||||
### 3. Function Calling
|
||||
|
||||
- **Purpose**: Custom tools integration
|
||||
- **Pattern**: requires_action → submit_tool_outputs
|
||||
- **Timeout**: Must respond within 10 minutes
|
||||
- **Parallel**: Multiple functions can be called at once
|
||||
|
||||
---
|
||||
|
||||
## Key Limits
|
||||
|
||||
| Resource | Limit |
|
||||
|----------|-------|
|
||||
| Assistant instructions | 256,000 characters |
|
||||
| Thread messages | 100,000 per thread |
|
||||
| Tools per assistant | 128 tools |
|
||||
| Vector store files | 10,000 per assistant |
|
||||
| File size | 512 MB per file |
|
||||
| Run execution time | 10 minutes |
|
||||
| Metadata pairs | 16 per object |
|
||||
|
||||
---
|
||||
|
||||
## Pricing
|
||||
|
||||
### API Calls
|
||||
- Same as Chat Completions (pay per token)
|
||||
- Run usage reported in `run.usage`
|
||||
|
||||
### Vector Stores
|
||||
- **Storage**: $0.10/GB/day
|
||||
- **Free tier**: First 1GB
|
||||
- **Auto-expiration**: Configurable
|
||||
|
||||
---
|
||||
|
||||
## Migration Timeline
|
||||
|
||||
- **✅ Dec 18, 2024**: v1 deprecated (no longer accessible)
|
||||
- **⏳ H1 2026**: v2 planned sunset
|
||||
- **✅ Now**: Responses API available (recommended replacement)
|
||||
|
||||
**Action**: Plan migration to Responses API for new projects.
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Reuse Assistants**: Create once, use many times
|
||||
2. **One Thread Per User**: Maintain conversation continuity
|
||||
3. **Check Active Runs**: Before creating new runs
|
||||
4. **Stream for UX**: Better user experience than polling
|
||||
5. **Set Timeouts**: Prevent infinite polling
|
||||
6. **Clean Up**: Delete old threads and vector stores
|
||||
7. **Monitor Costs**: Track token usage and storage
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Multi-User Chatbot
|
||||
```typescript
|
||||
const userThreads = new Map<string, string>();
|
||||
|
||||
async function getUserThread(userId: string) {
|
||||
if (!userThreads.has(userId)) {
|
||||
const thread = await openai.beta.threads.create({
|
||||
metadata: { user_id: userId },
|
||||
});
|
||||
userThreads.set(userId, thread.id);
|
||||
}
|
||||
return userThreads.get(userId)!;
|
||||
}
|
||||
```
|
||||
|
||||
### RAG Application
|
||||
```typescript
|
||||
// 1. Create vector store with documents
|
||||
const vectorStore = await openai.beta.vectorStores.create({...});
|
||||
await openai.beta.vectorStores.fileBatches.create(vectorStore.id, {...});
|
||||
|
||||
// 2. Create assistant with file_search
|
||||
const assistant = await openai.beta.assistants.create({
|
||||
tools: [{ type: "file_search" }],
|
||||
tool_resources: {
|
||||
file_search: { vector_store_ids: [vectorStore.id] },
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Data Analysis
|
||||
```typescript
|
||||
const assistant = await openai.beta.assistants.create({
|
||||
tools: [{ type: "code_interpreter" }],
|
||||
});
|
||||
|
||||
// Upload data
|
||||
const file = await openai.files.create({...});
|
||||
|
||||
// Attach to message
|
||||
await openai.beta.threads.messages.create(thread.id, {
|
||||
content: "Analyze this data",
|
||||
attachments: [{ file_id: file.id, tools: [{ type: "code_interpreter" }] }],
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Official Documentation
|
||||
|
||||
- **API Reference**: https://platform.openai.com/docs/api-reference/assistants
|
||||
- **Overview**: https://platform.openai.com/docs/assistants/overview
|
||||
- **Tools**: https://platform.openai.com/docs/assistants/tools
|
||||
- **Migration**: https://platform.openai.com/docs/assistants/whats-new
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: 2025-10-25
|
||||
338
references/code-interpreter-guide.md
Normal file
338
references/code-interpreter-guide.md
Normal file
@@ -0,0 +1,338 @@
|
||||
# Code Interpreter Guide
|
||||
|
||||
Complete guide to using Code Interpreter with the Assistants API.
|
||||
|
||||
---
|
||||
|
||||
## What is Code Interpreter?
|
||||
|
||||
A built-in tool that executes Python code in a sandboxed environment, enabling:
|
||||
- Data analysis and processing
|
||||
- Mathematical computations
|
||||
- Chart and graph generation
|
||||
- File parsing (CSV, JSON, Excel, etc.)
|
||||
- Data transformations
|
||||
|
||||
---
|
||||
|
||||
## Setup
|
||||
|
||||
```typescript
|
||||
const assistant = await openai.beta.assistants.create({
|
||||
name: "Data Analyst",
|
||||
instructions: "You analyze data and create visualizations.",
|
||||
tools: [{ type: "code_interpreter" }],
|
||||
model: "gpt-4o",
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## File Uploads
|
||||
|
||||
### Upload Data Files
|
||||
|
||||
```typescript
|
||||
const file = await openai.files.create({
|
||||
file: fs.createReadStream("data.csv"),
|
||||
purpose: "assistants",
|
||||
});
|
||||
```
|
||||
|
||||
### Attach to Messages
|
||||
|
||||
```typescript
|
||||
await openai.beta.threads.messages.create(thread.id, {
|
||||
role: "user",
|
||||
content: "Analyze this sales data",
|
||||
attachments: [{
|
||||
file_id: file.id,
|
||||
tools: [{ type: "code_interpreter" }],
|
||||
}],
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Supported File Formats
|
||||
|
||||
**Data Files**:
|
||||
- `.csv`, `.json`, `.xlsx` - Tabular data
|
||||
- `.txt`, `.md` - Text files
|
||||
- `.pdf`, `.docx`, `.pptx` - Documents (text extraction)
|
||||
|
||||
**Code Files**:
|
||||
- `.py`, `.js`, `.ts`, `.java`, `.cpp` - Source code
|
||||
|
||||
**Images** (for processing, not vision):
|
||||
- `.png`, `.jpg`, `.jpeg`, `.gif` - Image manipulation
|
||||
|
||||
**Archives**:
|
||||
- `.zip`, `.tar` - Compressed files
|
||||
|
||||
**Size Limit**: 512 MB per file
|
||||
|
||||
---
|
||||
|
||||
## Common Use Cases
|
||||
|
||||
### 1. Data Analysis
|
||||
|
||||
```typescript
|
||||
const thread = await openai.beta.threads.create({
|
||||
messages: [{
|
||||
role: "user",
|
||||
content: "Calculate the average, median, and standard deviation of the revenue column",
|
||||
attachments: [{
|
||||
file_id: csvFileId,
|
||||
tools: [{ type: "code_interpreter" }],
|
||||
}],
|
||||
}],
|
||||
});
|
||||
```
|
||||
|
||||
### 2. Data Visualization
|
||||
|
||||
```typescript
|
||||
await openai.beta.threads.messages.create(thread.id, {
|
||||
role: "user",
|
||||
content: "Create a line chart showing revenue over time",
|
||||
});
|
||||
|
||||
// After run completes, download the generated image
|
||||
const messages = await openai.beta.threads.messages.list(thread.id);
|
||||
for (const content of messages.data[0].content) {
|
||||
if (content.type === 'image_file') {
|
||||
const imageData = await openai.files.content(content.image_file.file_id);
|
||||
const buffer = Buffer.from(await imageData.arrayBuffer());
|
||||
fs.writeFileSync('chart.png', buffer);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. File Conversion
|
||||
|
||||
```typescript
|
||||
await openai.beta.threads.messages.create(thread.id, {
|
||||
role: "user",
|
||||
content: "Convert this Excel file to CSV format",
|
||||
attachments: [{
|
||||
file_id: excelFileId,
|
||||
tools: [{ type: "code_interpreter" }],
|
||||
}],
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Retrieving Outputs
|
||||
|
||||
### Text Output
|
||||
|
||||
```typescript
|
||||
const messages = await openai.beta.threads.messages.list(thread.id);
|
||||
const response = messages.data[0];
|
||||
|
||||
for (const content of response.content) {
|
||||
if (content.type === 'text') {
|
||||
console.log(content.text.value);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Generated Files (Charts, CSVs)
|
||||
|
||||
```typescript
|
||||
for (const content of response.content) {
|
||||
if (content.type === 'image_file') {
|
||||
const fileId = content.image_file.file_id;
|
||||
const data = await openai.files.content(fileId);
|
||||
const buffer = Buffer.from(await data.arrayBuffer());
|
||||
fs.writeFileSync(`output_${fileId}.png`, buffer);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Execution Logs
|
||||
|
||||
```typescript
|
||||
const runSteps = await openai.beta.threads.runs.steps.list(thread.id, run.id);
|
||||
|
||||
for (const step of runSteps.data) {
|
||||
if (step.step_details.type === 'tool_calls') {
|
||||
for (const toolCall of step.step_details.tool_calls) {
|
||||
if (toolCall.type === 'code_interpreter') {
|
||||
console.log('Code:', toolCall.code_interpreter.input);
|
||||
console.log('Output:', toolCall.code_interpreter.outputs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Python Environment
|
||||
|
||||
### Available Libraries
|
||||
|
||||
The Code Interpreter sandbox includes common libraries:
|
||||
- **Data**: pandas, numpy
|
||||
- **Math**: scipy, sympy
|
||||
- **Plotting**: matplotlib, seaborn
|
||||
- **ML**: scikit-learn (limited)
|
||||
- **Utils**: requests, PIL, csv, json
|
||||
|
||||
**Note**: Not all PyPI packages available. Use standard library where possible.
|
||||
|
||||
### Environment Limits
|
||||
|
||||
- **Execution Time**: Part of 10-minute run limit
|
||||
- **Memory**: Limited (exact amount not documented)
|
||||
- **Disk Space**: Files persist during run only
|
||||
- **Network**: No outbound internet access
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Clear Instructions
|
||||
|
||||
```typescript
|
||||
// ❌ Vague
|
||||
"Analyze the data"
|
||||
|
||||
// ✅ Specific
|
||||
"Calculate the mean, median, and mode for each numeric column. Create a bar chart comparing these metrics."
|
||||
```
|
||||
|
||||
### 2. File Download Immediately
|
||||
|
||||
```typescript
|
||||
// Generated files are temporary - download right after completion
|
||||
if (run.status === 'completed') {
|
||||
const messages = await openai.beta.threads.messages.list(thread.id);
|
||||
// Download all image files immediately
|
||||
for (const message of messages.data) {
|
||||
for (const content of message.content) {
|
||||
if (content.type === 'image_file') {
|
||||
await downloadFile(content.image_file.file_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Error Handling
|
||||
|
||||
```typescript
|
||||
const runSteps = await openai.beta.threads.runs.steps.list(thread.id, run.id);
|
||||
|
||||
for (const step of runSteps.data) {
|
||||
if (step.step_details.type === 'tool_calls') {
|
||||
for (const toolCall of step.step_details.tool_calls) {
|
||||
if (toolCall.type === 'code_interpreter') {
|
||||
const outputs = toolCall.code_interpreter.outputs;
|
||||
for (const output of outputs) {
|
||||
if (output.type === 'logs' && output.logs.includes('Error')) {
|
||||
console.error('Execution error:', output.logs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Pattern: Iterative Analysis
|
||||
|
||||
```typescript
|
||||
// 1. Upload data
|
||||
const file = await openai.files.create({...});
|
||||
|
||||
// 2. Initial analysis
|
||||
await sendMessage("What are the columns and data types?");
|
||||
|
||||
// 3. Follow-up based on results
|
||||
await sendMessage("Show the distribution of the 'category' column");
|
||||
|
||||
// 4. Visualization
|
||||
await sendMessage("Create a heatmap of correlations between numeric columns");
|
||||
```
|
||||
|
||||
### Pattern: Multi-File Processing
|
||||
|
||||
```typescript
|
||||
await openai.beta.threads.messages.create(thread.id, {
|
||||
role: "user",
|
||||
content: "Merge these two CSV files on the 'id' column",
|
||||
attachments: [
|
||||
{ file_id: file1Id, tools: [{ type: "code_interpreter" }] },
|
||||
{ file_id: file2Id, tools: [{ type: "code_interpreter" }] },
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Issue: Code Execution Fails
|
||||
|
||||
**Symptoms**: Run completes but no output/error in logs
|
||||
|
||||
**Solutions**:
|
||||
- Check file format compatibility
|
||||
- Verify file isn't corrupted
|
||||
- Ensure data is in expected format (headers, encoding)
|
||||
- Try simpler request first to verify setup
|
||||
|
||||
### Issue: Generated Files Not Found
|
||||
|
||||
**Symptoms**: `image_file.file_id` doesn't exist
|
||||
|
||||
**Solutions**:
|
||||
- Download immediately after run completes
|
||||
- Check run steps for actual outputs
|
||||
- Verify code execution succeeded
|
||||
|
||||
### Issue: Timeout on Large Files
|
||||
|
||||
**Symptoms**: Run exceeds 10-minute limit
|
||||
|
||||
**Solutions**:
|
||||
- Split large files into smaller chunks
|
||||
- Request specific analysis (not "analyze everything")
|
||||
- Use sampling for exploratory analysis
|
||||
|
||||
---
|
||||
|
||||
## Example Prompts
|
||||
|
||||
**Data Exploration**:
|
||||
- "Summarize this dataset: shape, columns, data types, missing values"
|
||||
- "Show the first 10 rows"
|
||||
- "What are the unique values in the 'status' column?"
|
||||
|
||||
**Statistical Analysis**:
|
||||
- "Calculate descriptive statistics for all numeric columns"
|
||||
- "Perform correlation analysis between price and quantity"
|
||||
- "Detect outliers using the IQR method"
|
||||
|
||||
**Visualization**:
|
||||
- "Create a histogram of the 'age' distribution"
|
||||
- "Plot revenue trends over time with a moving average"
|
||||
- "Generate a scatter plot of height vs weight, colored by gender"
|
||||
|
||||
**Data Transformation**:
|
||||
- "Remove rows with missing values"
|
||||
- "Normalize the 'sales' column to 0-1 range"
|
||||
- "Convert dates to YYYY-MM-DD format"
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: 2025-10-25
|
||||
364
references/file-search-rag-guide.md
Normal file
364
references/file-search-rag-guide.md
Normal file
@@ -0,0 +1,364 @@
|
||||
# File Search & RAG Guide
|
||||
|
||||
Complete guide to implementing Retrieval-Augmented Generation (RAG) with the Assistants API.
|
||||
|
||||
---
|
||||
|
||||
## What is File Search?
|
||||
|
||||
A built-in tool for semantic search over documents using vector stores:
|
||||
- **Capacity**: Up to 10,000 files per assistant (vs 20 in v1)
|
||||
- **Technology**: Vector + keyword search with reranking
|
||||
- **Automatic**: Chunking, embedding, and indexing handled by OpenAI
|
||||
- **Pricing**: $0.10/GB/day (first 1GB free)
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
Documents (PDF, DOCX, MD, etc.)
|
||||
↓
|
||||
Vector Store (chunking + embeddings)
|
||||
↓
|
||||
Assistant with file_search tool
|
||||
↓
|
||||
Semantic Search + Reranking
|
||||
↓
|
||||
Retrieved Context + LLM Generation
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick Setup
|
||||
|
||||
### 1. Create Vector Store
|
||||
|
||||
```typescript
|
||||
const vectorStore = await openai.beta.vectorStores.create({
|
||||
name: "Product Documentation",
|
||||
expires_after: {
|
||||
anchor: "last_active_at",
|
||||
days: 30,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### 2. Upload Documents
|
||||
|
||||
```typescript
|
||||
const files = await Promise.all([
|
||||
openai.files.create({ file: fs.createReadStream("doc1.pdf"), purpose: "assistants" }),
|
||||
openai.files.create({ file: fs.createReadStream("doc2.md"), purpose: "assistants" }),
|
||||
]);
|
||||
|
||||
const batch = await openai.beta.vectorStores.fileBatches.create(vectorStore.id, {
|
||||
file_ids: files.map(f => f.id),
|
||||
});
|
||||
```
|
||||
|
||||
### 3. Wait for Indexing
|
||||
|
||||
```typescript
|
||||
let batch = await openai.beta.vectorStores.fileBatches.retrieve(vectorStore.id, batch.id);
|
||||
|
||||
while (batch.status === 'in_progress') {
|
||||
await new Promise(r => setTimeout(r, 2000));
|
||||
batch = await openai.beta.vectorStores.fileBatches.retrieve(vectorStore.id, batch.id);
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Create Assistant
|
||||
|
||||
```typescript
|
||||
const assistant = await openai.beta.assistants.create({
|
||||
name: "Knowledge Base Assistant",
|
||||
instructions: "Answer questions using the file search tool. Always cite your sources.",
|
||||
tools: [{ type: "file_search" }],
|
||||
tool_resources: {
|
||||
file_search: {
|
||||
vector_store_ids: [vectorStore.id],
|
||||
},
|
||||
},
|
||||
model: "gpt-4o",
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Supported File Formats
|
||||
|
||||
- `.pdf` - PDFs (most common)
|
||||
- `.docx` - Word documents
|
||||
- `.md`, `.txt` - Plain text
|
||||
- `.html` - HTML documents
|
||||
- `.json` - JSON data
|
||||
- `.py`, `.js`, `.ts`, `.cpp`, `.java` - Code files
|
||||
|
||||
**Size Limits**:
|
||||
- **Per file**: 512 MB
|
||||
- **Total per vector store**: Limited by pricing ($0.10/GB/day)
|
||||
|
||||
---
|
||||
|
||||
## Chunking Strategy
|
||||
|
||||
OpenAI automatically chunks documents using:
|
||||
- **Max chunk size**: ~800 tokens (configurable internally)
|
||||
- **Overlap**: Ensures context continuity
|
||||
- **Hierarchy**: Preserves document structure (headers, sections)
|
||||
|
||||
### Optimize for Better Results
|
||||
|
||||
**Document Structure**:
|
||||
```markdown
|
||||
# Main Topic
|
||||
|
||||
## Subtopic 1
|
||||
Content here...
|
||||
|
||||
## Subtopic 2
|
||||
Content here...
|
||||
```
|
||||
|
||||
**Clear Sections**: Use headers to organize content
|
||||
**Concise Paragraphs**: Avoid very long paragraphs (500+ words)
|
||||
**Self-Contained**: Each section should make sense independently
|
||||
|
||||
---
|
||||
|
||||
## Improving Search Quality
|
||||
|
||||
### 1. Better Instructions
|
||||
|
||||
```typescript
|
||||
const assistant = await openai.beta.assistants.create({
|
||||
instructions: `You are a support assistant. When answering:
|
||||
1. Use file_search to find relevant information
|
||||
2. Synthesize information from multiple sources
|
||||
3. Always provide citations with file names
|
||||
4. If information isn't found, say so clearly
|
||||
5. Don't make up information not in the documents`,
|
||||
tools: [{ type: "file_search" }],
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
### 2. Query Refinement
|
||||
|
||||
Encourage users to be specific:
|
||||
- ❌ "How do I install?"
|
||||
- ✅ "How do I install the product on Windows 10?"
|
||||
|
||||
### 3. Multi-Document Answers
|
||||
|
||||
File Search automatically retrieves from multiple documents and combines information.
|
||||
|
||||
---
|
||||
|
||||
## Citations
|
||||
|
||||
### Accessing Citations
|
||||
|
||||
```typescript
|
||||
const messages = await openai.beta.threads.messages.list(thread.id);
|
||||
const response = messages.data[0];
|
||||
|
||||
for (const content of response.content) {
|
||||
if (content.type === 'text') {
|
||||
console.log('Answer:', content.text.value);
|
||||
|
||||
// Citations
|
||||
if (content.text.annotations) {
|
||||
for (const annotation of content.text.annotations) {
|
||||
if (annotation.type === 'file_citation') {
|
||||
console.log('Source:', annotation.file_citation.file_id);
|
||||
console.log('Quote:', annotation.file_citation.quote);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Displaying Citations
|
||||
|
||||
```typescript
|
||||
let answer = response.content[0].text.value;
|
||||
|
||||
// Replace citation markers with clickable links
|
||||
for (const annotation of response.content[0].text.annotations) {
|
||||
if (annotation.type === 'file_citation') {
|
||||
const citation = `[${annotation.text}](source: ${annotation.file_citation.file_id})`;
|
||||
answer = answer.replace(annotation.text, citation);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(answer);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Cost Management
|
||||
|
||||
### Pricing Structure
|
||||
|
||||
- **Storage**: $0.10/GB/day
|
||||
- **Free tier**: First 1GB
|
||||
- **Example**: 5GB = $0.40/day = $12/month
|
||||
|
||||
### Optimization Strategies
|
||||
|
||||
1. **Auto-Expiration**:
|
||||
```typescript
|
||||
const vectorStore = await openai.beta.vectorStores.create({
|
||||
expires_after: {
|
||||
anchor: "last_active_at",
|
||||
days: 7, // Delete after 7 days of inactivity
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
2. **Cleanup Old Stores**:
|
||||
```typescript
|
||||
async function cleanupOldVectorStores() {
|
||||
const stores = await openai.beta.vectorStores.list({ limit: 100 });
|
||||
|
||||
for (const store of stores.data) {
|
||||
const ageDays = (Date.now() / 1000 - store.created_at) / (60 * 60 * 24);
|
||||
|
||||
if (ageDays > 30) {
|
||||
await openai.beta.vectorStores.del(store.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. **Monitor Usage**:
|
||||
```typescript
|
||||
const store = await openai.beta.vectorStores.retrieve(vectorStoreId);
|
||||
const sizeGB = store.usage_bytes / (1024 * 1024 * 1024);
|
||||
const costPerDay = Math.max(0, (sizeGB - 1) * 0.10);
|
||||
console.log(`Daily cost: $${costPerDay.toFixed(4)}`);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Advanced Patterns
|
||||
|
||||
### Pattern: Multi-Tenant Knowledge Bases
|
||||
|
||||
```typescript
|
||||
// Separate vector store per tenant
|
||||
const tenantStore = await openai.beta.vectorStores.create({
|
||||
name: `Tenant ${tenantId} KB`,
|
||||
metadata: { tenant_id: tenantId },
|
||||
});
|
||||
|
||||
// Or: Single store with namespace simulation via file metadata
|
||||
await openai.files.create({
|
||||
file: fs.createReadStream("doc.pdf"),
|
||||
purpose: "assistants",
|
||||
metadata: { tenant_id: tenantId }, // Coming soon
|
||||
});
|
||||
```
|
||||
|
||||
### Pattern: Versioned Documentation
|
||||
|
||||
```typescript
|
||||
// Version 1.0
|
||||
const v1Store = await openai.beta.vectorStores.create({
|
||||
name: "Docs v1.0",
|
||||
metadata: { version: "1.0" },
|
||||
});
|
||||
|
||||
// Version 2.0
|
||||
const v2Store = await openai.beta.vectorStores.create({
|
||||
name: "Docs v2.0",
|
||||
metadata: { version: "2.0" },
|
||||
});
|
||||
|
||||
// Switch based on user preference
|
||||
const storeId = userVersion === "1.0" ? v1Store.id : v2Store.id;
|
||||
```
|
||||
|
||||
### Pattern: Hybrid Search (File Search + Code Interpreter)
|
||||
|
||||
```typescript
|
||||
const assistant = await openai.beta.assistants.create({
|
||||
tools: [
|
||||
{ type: "file_search" },
|
||||
{ type: "code_interpreter" },
|
||||
],
|
||||
tool_resources: {
|
||||
file_search: {
|
||||
vector_store_ids: [docsVectorStoreId],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Assistant can search docs AND analyze attached data files
|
||||
await openai.beta.threads.messages.create(thread.id, {
|
||||
content: "Compare this sales data against the targets in our planning docs",
|
||||
attachments: [{
|
||||
file_id: salesDataFileId,
|
||||
tools: [{ type: "code_interpreter" }],
|
||||
}],
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### No Results Found
|
||||
|
||||
**Causes**:
|
||||
- Vector store not fully indexed
|
||||
- Poor query formulation
|
||||
- Documents lack relevant content
|
||||
|
||||
**Solutions**:
|
||||
- Wait for `status: "completed"`
|
||||
- Refine query to be more specific
|
||||
- Check document quality and structure
|
||||
|
||||
### Irrelevant Results
|
||||
|
||||
**Causes**:
|
||||
- Poor document structure
|
||||
- Too much noise in documents
|
||||
- Vague queries
|
||||
|
||||
**Solutions**:
|
||||
- Add clear section headers
|
||||
- Remove boilerplate/repetitive content
|
||||
- Improve query specificity
|
||||
|
||||
### High Costs
|
||||
|
||||
**Causes**:
|
||||
- Too many vector stores
|
||||
- Large files that don't expire
|
||||
- Duplicate content
|
||||
|
||||
**Solutions**:
|
||||
- Set auto-expiration
|
||||
- Deduplicate documents
|
||||
- Delete unused stores
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Structure documents** with clear headers and sections
|
||||
2. **Wait for indexing** before using vector store
|
||||
3. **Set auto-expiration** to manage costs
|
||||
4. **Monitor storage** regularly
|
||||
5. **Provide citations** in responses
|
||||
6. **Refine queries** for better results
|
||||
7. **Clean up** old vector stores
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: 2025-10-25
|
||||
188
references/migration-from-v1.md
Normal file
188
references/migration-from-v1.md
Normal file
@@ -0,0 +1,188 @@
|
||||
# Migration from Assistants API v1 to v2
|
||||
|
||||
**v1 Deprecated**: December 18, 2024 (no longer accessible)
|
||||
**v2 Status**: Production (Deprecated H1 2026 in favor of Responses API)
|
||||
|
||||
---
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
### 1. Retrieval Tool → File Search
|
||||
|
||||
**v1:**
|
||||
```typescript
|
||||
{
|
||||
tools: [{ type: "retrieval" }],
|
||||
file_ids: ["file_abc123", "file_def456"]
|
||||
}
|
||||
```
|
||||
|
||||
**v2:**
|
||||
```typescript
|
||||
{
|
||||
tools: [{ type: "file_search" }],
|
||||
tool_resources: {
|
||||
file_search: {
|
||||
vector_store_ids: ["vs_abc123"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Action**: Create vector stores and migrate files.
|
||||
|
||||
### 2. File Attachments
|
||||
|
||||
**v1**: Files attached at assistant level
|
||||
**v2**: Files attached at message level
|
||||
|
||||
**v1:**
|
||||
```typescript
|
||||
const assistant = await openai.beta.assistants.create({
|
||||
file_ids: ["file_abc123"],
|
||||
});
|
||||
```
|
||||
|
||||
**v2:**
|
||||
```typescript
|
||||
await openai.beta.threads.messages.create(thread.id, {
|
||||
content: "...",
|
||||
attachments: [{
|
||||
file_id: "file_abc123",
|
||||
tools: [{ type: "code_interpreter" }]
|
||||
}],
|
||||
});
|
||||
```
|
||||
|
||||
### 3. Instructions Character Limit
|
||||
|
||||
- **v1**: 32,000 characters
|
||||
- **v2**: 256,000 characters (8x increase)
|
||||
|
||||
---
|
||||
|
||||
## Migration Steps
|
||||
|
||||
### Step 1: Create Vector Stores
|
||||
|
||||
```typescript
|
||||
// Old v1 approach
|
||||
const assistant = await openai.beta.assistants.create({
|
||||
tools: [{ type: "retrieval" }],
|
||||
file_ids: fileIds, // Direct attachment
|
||||
});
|
||||
|
||||
// New v2 approach
|
||||
const vectorStore = await openai.beta.vectorStores.create({
|
||||
name: "Knowledge Base",
|
||||
});
|
||||
|
||||
await openai.beta.vectorStores.fileBatches.create(vectorStore.id, {
|
||||
file_ids: fileIds,
|
||||
});
|
||||
|
||||
const assistant = await openai.beta.assistants.create({
|
||||
tools: [{ type: "file_search" }],
|
||||
tool_resources: {
|
||||
file_search: {
|
||||
vector_store_ids: [vectorStore.id],
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Step 2: Update File Attachments
|
||||
|
||||
```typescript
|
||||
// Move file attachments from assistant to messages
|
||||
await openai.beta.threads.messages.create(thread.id, {
|
||||
role: "user",
|
||||
content: "Analyze this file",
|
||||
attachments: [{
|
||||
file_id: "file_abc123",
|
||||
tools: [{ type: "code_interpreter" }],
|
||||
}],
|
||||
});
|
||||
```
|
||||
|
||||
### Step 3: Test Thoroughly
|
||||
|
||||
- Verify file search returns expected results
|
||||
- Check Code Interpreter file handling
|
||||
- Test streaming if used
|
||||
- Validate function calling patterns
|
||||
|
||||
---
|
||||
|
||||
## New v2 Features
|
||||
|
||||
### 1. Massive File Capacity
|
||||
|
||||
- **v1**: ~20 files per assistant
|
||||
- **v2**: 10,000 files per assistant (500x increase)
|
||||
|
||||
### 2. Better Search Performance
|
||||
|
||||
- Vector + keyword search
|
||||
- Parallel query processing
|
||||
- Advanced reranking
|
||||
|
||||
### 3. Auto-Expiration
|
||||
|
||||
```typescript
|
||||
const vectorStore = await openai.beta.vectorStores.create({
|
||||
expires_after: {
|
||||
anchor: "last_active_at",
|
||||
days: 30,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### 4. Batch File Operations
|
||||
|
||||
```typescript
|
||||
const batch = await openai.beta.vectorStores.fileBatches.create(vectorStoreId, {
|
||||
file_ids: ["file_1", "file_2", "file_3"],
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Cost Implications
|
||||
|
||||
### v1 (Deprecated)
|
||||
- No separate storage costs for retrieval
|
||||
|
||||
### v2
|
||||
- **Storage**: $0.10/GB/day for vector stores
|
||||
- **Free tier**: First 1GB
|
||||
- **Optimization**: Use auto-expiration
|
||||
|
||||
---
|
||||
|
||||
## Recommended Path Forward
|
||||
|
||||
**For existing v1 applications:**
|
||||
1. Migrate to v2 immediately (v1 no longer works)
|
||||
2. Plan migration to Responses API (v2 sunset in H1 2026)
|
||||
|
||||
**For new applications:**
|
||||
- ✅ Use [Responses API](../../openai-responses/SKILL.md)
|
||||
- ❌ Don't use Assistants API (being deprecated)
|
||||
|
||||
---
|
||||
|
||||
## Migration Checklist
|
||||
|
||||
- [ ] Update to openai SDK 6.7.0+
|
||||
- [ ] Create vector stores for file search
|
||||
- [ ] Migrate file attachments to message level
|
||||
- [ ] Test file search results
|
||||
- [ ] Update to `file_search` from `retrieval`
|
||||
- [ ] Implement vector store cleanup
|
||||
- [ ] Monitor storage costs
|
||||
- [ ] Plan migration to Responses API
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: 2025-10-25
|
||||
271
references/thread-lifecycle.md
Normal file
271
references/thread-lifecycle.md
Normal file
@@ -0,0 +1,271 @@
|
||||
# Thread Lifecycle Management
|
||||
|
||||
Complete guide to managing threads effectively to avoid errors and optimize performance.
|
||||
|
||||
---
|
||||
|
||||
## Thread States
|
||||
|
||||
A thread progresses through these states based on run activity:
|
||||
|
||||
```
|
||||
Idle (no active runs)
|
||||
↓
|
||||
Active (run in progress)
|
||||
↓
|
||||
Requires Action (function calling)
|
||||
↓
|
||||
Completed / Failed / Cancelled
|
||||
↓
|
||||
Idle (ready for next run)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Pattern 1: One Thread Per User
|
||||
|
||||
**Use Case**: Chatbots, support assistants
|
||||
|
||||
```typescript
|
||||
// In-memory cache (use database in production)
|
||||
const userThreads = new Map<string, string>();
|
||||
|
||||
async function getOrCreateUserThread(userId: string): Promise<string> {
|
||||
let threadId = userThreads.get(userId);
|
||||
|
||||
if (!threadId) {
|
||||
const thread = await openai.beta.threads.create({
|
||||
metadata: { user_id: userId },
|
||||
});
|
||||
threadId = thread.id;
|
||||
userThreads.set(userId, threadId);
|
||||
}
|
||||
|
||||
return threadId;
|
||||
}
|
||||
```
|
||||
|
||||
**Benefits**:
|
||||
- Conversation continuity
|
||||
- Automatic history management
|
||||
- Simple architecture
|
||||
|
||||
**Drawbacks**:
|
||||
- Long threads consume memory
|
||||
- 100k message limit eventually
|
||||
|
||||
### Pattern 2: Session-Based Threads
|
||||
|
||||
**Use Case**: Temporary conversations
|
||||
|
||||
```typescript
|
||||
async function createSessionThread(userId: string, sessionId: string) {
|
||||
return await openai.beta.threads.create({
|
||||
metadata: {
|
||||
user_id: userId,
|
||||
session_id: sessionId,
|
||||
expires_at: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(),
|
||||
},
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
**Benefits**:
|
||||
- Clear boundaries
|
||||
- Easy cleanup
|
||||
- Fresh context
|
||||
|
||||
### Pattern 3: Topic-Based Threads
|
||||
|
||||
**Use Case**: Multi-topic conversations
|
||||
|
||||
```typescript
|
||||
async function getTopicThread(userId: string, topic: string) {
|
||||
const key = `${userId}:${topic}`;
|
||||
// Separate threads for different topics
|
||||
return await getOrCreateThread(key);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Active Run Management
|
||||
|
||||
### Check for Active Runs
|
||||
|
||||
```typescript
|
||||
async function hasActiveRun(threadId: string): Promise<boolean> {
|
||||
const runs = await openai.beta.threads.runs.list(threadId, {
|
||||
limit: 1,
|
||||
order: 'desc',
|
||||
});
|
||||
|
||||
const latestRun = runs.data[0];
|
||||
return latestRun && ['queued', 'in_progress', 'cancelling'].includes(latestRun.status);
|
||||
}
|
||||
```
|
||||
|
||||
### Safe Run Creation
|
||||
|
||||
```typescript
|
||||
async function createRunSafely(
|
||||
threadId: string,
|
||||
assistantId: string,
|
||||
cancelIfActive = true
|
||||
) {
|
||||
// Check for active runs
|
||||
const runs = await openai.beta.threads.runs.list(threadId, {
|
||||
limit: 1,
|
||||
order: 'desc',
|
||||
});
|
||||
|
||||
const latestRun = runs.data[0];
|
||||
if (latestRun && ['queued', 'in_progress'].includes(latestRun.status)) {
|
||||
if (cancelIfActive) {
|
||||
await openai.beta.threads.runs.cancel(threadId, latestRun.id);
|
||||
// Wait for cancellation
|
||||
while (latestRun.status !== 'cancelled') {
|
||||
await new Promise(r => setTimeout(r, 500));
|
||||
latestRun = await openai.beta.threads.runs.retrieve(threadId, latestRun.id);
|
||||
}
|
||||
} else {
|
||||
throw new Error('Thread has active run');
|
||||
}
|
||||
}
|
||||
|
||||
return await openai.beta.threads.runs.create(threadId, {
|
||||
assistant_id: assistantId,
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Cleanup Strategies
|
||||
|
||||
### Time-Based Cleanup
|
||||
|
||||
```typescript
|
||||
async function cleanupOldThreads(maxAgeHours: number = 24) {
|
||||
// Query your database for old threads
|
||||
const oldThreads = await db.getThreadsOlderThan(maxAgeHours);
|
||||
|
||||
for (const threadId of oldThreads) {
|
||||
await openai.beta.threads.del(threadId);
|
||||
await db.deleteThreadRecord(threadId);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Message Count-Based
|
||||
|
||||
```typescript
|
||||
async function archiveIfTooLong(threadId: string, maxMessages: number = 1000) {
|
||||
const messages = await openai.beta.threads.messages.list(threadId);
|
||||
|
||||
if (messages.data.length >= maxMessages) {
|
||||
// Archive to database
|
||||
await db.archiveThread(threadId, messages.data);
|
||||
|
||||
// Create new thread
|
||||
return await openai.beta.threads.create({
|
||||
metadata: { previous_thread: threadId },
|
||||
});
|
||||
}
|
||||
|
||||
return threadId;
|
||||
}
|
||||
```
|
||||
|
||||
### Safe Deletion
|
||||
|
||||
```typescript
|
||||
async function safeDeleteThread(threadId: string) {
|
||||
// Cancel all active runs first
|
||||
const runs = await openai.beta.threads.runs.list(threadId);
|
||||
|
||||
for (const run of runs.data) {
|
||||
if (['queued', 'in_progress'].includes(run.status)) {
|
||||
await openai.beta.threads.runs.cancel(threadId, run.id);
|
||||
}
|
||||
}
|
||||
|
||||
// Wait a moment for cancellations
|
||||
await new Promise(r => setTimeout(r, 1000));
|
||||
|
||||
// Delete thread
|
||||
await openai.beta.threads.del(threadId);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Concurrent Run Prevention
|
||||
|
||||
```typescript
|
||||
class ThreadManager {
|
||||
private locks = new Map<string, Promise<any>>();
|
||||
|
||||
async executeRun(threadId: string, assistantId: string) {
|
||||
// Wait if another run is in progress
|
||||
if (this.locks.has(threadId)) {
|
||||
await this.locks.get(threadId);
|
||||
}
|
||||
|
||||
// Create lock
|
||||
const runPromise = this._runAssistant(threadId, assistantId);
|
||||
this.locks.set(threadId, runPromise);
|
||||
|
||||
try {
|
||||
return await runPromise;
|
||||
} finally {
|
||||
this.locks.delete(threadId);
|
||||
}
|
||||
}
|
||||
|
||||
private async _runAssistant(threadId: string, assistantId: string) {
|
||||
// Run logic here
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Always check for active runs** before creating new ones
|
||||
2. **Use metadata** to track ownership and expiration
|
||||
3. **Implement cleanup** to manage costs
|
||||
4. **Set reasonable limits** (message count, age)
|
||||
5. **Handle errors gracefully** (active run conflicts)
|
||||
6. **Archive old conversations** before deletion
|
||||
7. **Use locks** for concurrent access
|
||||
|
||||
---
|
||||
|
||||
## Monitoring
|
||||
|
||||
```typescript
|
||||
async function getThreadStats(threadId: string) {
|
||||
const thread = await openai.beta.threads.retrieve(threadId);
|
||||
const messages = await openai.beta.threads.messages.list(threadId);
|
||||
const runs = await openai.beta.threads.runs.list(threadId);
|
||||
|
||||
return {
|
||||
threadId: thread.id,
|
||||
createdAt: new Date(thread.created_at * 1000),
|
||||
messageCount: messages.data.length,
|
||||
runCount: runs.data.length,
|
||||
lastRun: runs.data[0],
|
||||
metadata: thread.metadata,
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: 2025-10-25
|
||||
446
references/top-errors.md
Normal file
446
references/top-errors.md
Normal file
@@ -0,0 +1,446 @@
|
||||
# Top 12 Errors - OpenAI Assistants API
|
||||
|
||||
**Last Updated**: 2025-10-25
|
||||
|
||||
This document catalogs the most common errors encountered when working with the Assistants API v2 and their solutions.
|
||||
|
||||
---
|
||||
|
||||
## 1. Thread Already Has Active Run
|
||||
|
||||
**Error Message**:
|
||||
```
|
||||
Error: 400 Can't add messages to thread_xxx while a run run_xxx is active.
|
||||
```
|
||||
|
||||
**Cause**: Trying to create a new run or add messages while another run is still processing (status: `queued`, `in_progress`, or `cancelling`).
|
||||
|
||||
**Solution**:
|
||||
```typescript
|
||||
async function ensureNoActiveRun(threadId: string) {
|
||||
const runs = await openai.beta.threads.runs.list(threadId, {
|
||||
limit: 1,
|
||||
order: 'desc',
|
||||
});
|
||||
|
||||
const latestRun = runs.data[0];
|
||||
if (latestRun && ['queued', 'in_progress', 'cancelling'].includes(latestRun.status)) {
|
||||
// Wait for completion or cancel
|
||||
await openai.beta.threads.runs.cancel(threadId, latestRun.id);
|
||||
|
||||
// Poll until cancelled
|
||||
let run = latestRun;
|
||||
while (run.status !== 'cancelled') {
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
run = await openai.beta.threads.runs.retrieve(threadId, run.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Prevention**: Always check for active runs before creating new ones.
|
||||
|
||||
**Source**: [OpenAI Community](https://community.openai.com/t/error-running-thread-already-has-an-active-run/782118)
|
||||
|
||||
---
|
||||
|
||||
## 2. Run Polling Timeout
|
||||
|
||||
**Error**: Run never completes within reasonable polling window (300+ seconds).
|
||||
|
||||
**Cause**: Long-running tasks (complex code execution, large file processing) exceed expected completion time.
|
||||
|
||||
**Solution**:
|
||||
```typescript
|
||||
async function pollWithTimeout(threadId: string, runId: string, maxSeconds = 300) {
|
||||
const startTime = Date.now();
|
||||
|
||||
while (true) {
|
||||
const run = await openai.beta.threads.runs.retrieve(threadId, runId);
|
||||
|
||||
if (!['queued', 'in_progress'].includes(run.status)) {
|
||||
return run;
|
||||
}
|
||||
|
||||
const elapsed = (Date.now() - startTime) / 1000;
|
||||
if (elapsed > maxSeconds) {
|
||||
await openai.beta.threads.runs.cancel(threadId, runId);
|
||||
throw new Error(`Run exceeded timeout of ${maxSeconds}s`);
|
||||
}
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Prevention**: Set appropriate timeouts and use streaming for better UX.
|
||||
|
||||
---
|
||||
|
||||
## 3. Vector Store Indexing Delay
|
||||
|
||||
**Error**: File search returns no results despite files being uploaded.
|
||||
|
||||
**Cause**: Using vector store before indexing completes (async process).
|
||||
|
||||
**Solution**:
|
||||
```typescript
|
||||
async function waitForVectorStore(vectorStoreId: string) {
|
||||
let store = await openai.beta.vectorStores.retrieve(vectorStoreId);
|
||||
|
||||
while (store.status === 'in_progress') {
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
store = await openai.beta.vectorStores.retrieve(vectorStoreId);
|
||||
console.log(`Indexing: ${store.file_counts.completed}/${store.file_counts.total}`);
|
||||
}
|
||||
|
||||
if (store.status === 'failed') {
|
||||
throw new Error('Vector store indexing failed');
|
||||
}
|
||||
|
||||
return store; // status: 'completed'
|
||||
}
|
||||
```
|
||||
|
||||
**Prevention**: Always wait for `status: "completed"` before using vector store with assistants.
|
||||
|
||||
**Source**: [OpenAI Community](https://community.openai.com/t/assistants-api-file-search-and-vector-stores/863944)
|
||||
|
||||
---
|
||||
|
||||
## 4. File Search Relevance Issues
|
||||
|
||||
**Error**: File search returns irrelevant or incomplete results.
|
||||
|
||||
**Cause**: Poor document chunking, lack of context, or query optimization needed.
|
||||
|
||||
**Solution**:
|
||||
- **Better instructions**: Guide assistant on how to use file search
|
||||
- **Structured documents**: Use clear headers, sections, and formatting
|
||||
- **Metadata**: Add descriptive metadata to files (coming soon)
|
||||
- **Query refinement**: Encourage users to be specific
|
||||
|
||||
```typescript
|
||||
const assistant = await openai.beta.assistants.create({
|
||||
instructions: `You are a support assistant. When answering:
|
||||
1. Use file_search to find relevant documentation
|
||||
2. Quote specific sections with citations
|
||||
3. If information isn't found, say so clearly
|
||||
4. Provide context around the answer`,
|
||||
tools: [{ type: "file_search" }],
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
**Prevention**: Structure documents well and provide clear assistant instructions.
|
||||
|
||||
---
|
||||
|
||||
## 5. Code Interpreter File Output Not Found
|
||||
|
||||
**Error**: `image_file.file_id` referenced but file doesn't exist or can't be downloaded.
|
||||
|
||||
**Cause**: Files generated by Code Interpreter are temporary and may be cleaned up before retrieval.
|
||||
|
||||
**Solution**:
|
||||
```typescript
|
||||
// Retrieve and save immediately after run completes
|
||||
const messages = await openai.beta.threads.messages.list(threadId);
|
||||
const responseMessage = messages.data[0];
|
||||
|
||||
for (const content of responseMessage.content) {
|
||||
if (content.type === 'image_file') {
|
||||
try {
|
||||
const fileData = await openai.files.content(content.image_file.file_id);
|
||||
const buffer = Buffer.from(await fileData.arrayBuffer());
|
||||
fs.writeFileSync(`output_${content.image_file.file_id}.png`, buffer);
|
||||
} catch (error) {
|
||||
console.error('File no longer available:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Prevention**: Download generated files immediately after run completion.
|
||||
|
||||
**Source**: [Medium - Code Interpreter Tutorial](https://tmmtt.medium.com/openai-assistant-api-with-code-interpreter-e7f382bff83e)
|
||||
|
||||
---
|
||||
|
||||
## 6. Thread Message Limit Exceeded
|
||||
|
||||
**Error**: `400 Thread has exceeded the maximum number of messages (100,000)`.
|
||||
|
||||
**Cause**: Very long conversations hitting the 100k message limit.
|
||||
|
||||
**Solution**:
|
||||
```typescript
|
||||
async function archiveAndStartNewThread(oldThreadId: string, userId: string) {
|
||||
// Get conversation summary
|
||||
const messages = await openai.beta.threads.messages.list(oldThreadId, {
|
||||
limit: 50,
|
||||
});
|
||||
|
||||
// Save to database
|
||||
await db.archiveThread(oldThreadId, messages.data);
|
||||
|
||||
// Create new thread
|
||||
const newThread = await openai.beta.threads.create({
|
||||
metadata: {
|
||||
user_id: userId,
|
||||
previous_thread: oldThreadId,
|
||||
},
|
||||
});
|
||||
|
||||
return newThread.id;
|
||||
}
|
||||
```
|
||||
|
||||
**Prevention**: Archive old threads and create new ones periodically.
|
||||
|
||||
---
|
||||
|
||||
## 7. Function Calling Timeout
|
||||
|
||||
**Error**: Run expires (status: `expired`) while waiting for tool outputs.
|
||||
|
||||
**Cause**: Tool execution takes too long (max 10 minutes for run).
|
||||
|
||||
**Solution**:
|
||||
```typescript
|
||||
if (run.status === 'requires_action') {
|
||||
const toolCalls = run.required_action.submit_tool_outputs.tool_calls;
|
||||
const toolOutputs = [];
|
||||
|
||||
for (const toolCall of toolCalls) {
|
||||
try {
|
||||
// Add timeout to function execution
|
||||
const output = await Promise.race([
|
||||
executeFunction(toolCall.function.name, JSON.parse(toolCall.function.arguments)),
|
||||
new Promise((_, reject) =>
|
||||
setTimeout(() => reject(new Error('Function timeout')), 30000)
|
||||
),
|
||||
]);
|
||||
|
||||
toolOutputs.push({
|
||||
tool_call_id: toolCall.id,
|
||||
output: JSON.stringify(output),
|
||||
});
|
||||
} catch (error) {
|
||||
// Return error as output
|
||||
toolOutputs.push({
|
||||
tool_call_id: toolCall.id,
|
||||
output: JSON.stringify({ error: error.message }),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
await openai.beta.threads.runs.submitToolOutputs(threadId, run.id, {
|
||||
tool_outputs: toolOutputs,
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
**Prevention**: Implement timeouts on function execution and return errors gracefully.
|
||||
|
||||
---
|
||||
|
||||
## 8. Streaming Run Interruption
|
||||
|
||||
**Error**: Stream connection closes unexpectedly or events stop arriving.
|
||||
|
||||
**Cause**: Network issues, server errors, or run failures.
|
||||
|
||||
**Solution**:
|
||||
```typescript
|
||||
async function streamWithRetry(threadId: string, assistantId: string) {
|
||||
try {
|
||||
const stream = await openai.beta.threads.runs.stream(threadId, {
|
||||
assistant_id: assistantId,
|
||||
});
|
||||
|
||||
for await (const event of stream) {
|
||||
// Handle events
|
||||
if (event.event === 'error') {
|
||||
throw new Error('Stream error');
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Stream interrupted:', error);
|
||||
|
||||
// Fall back to polling
|
||||
const runs = await openai.beta.threads.runs.list(threadId, {
|
||||
limit: 1,
|
||||
order: 'desc',
|
||||
});
|
||||
|
||||
const run = runs.data[0];
|
||||
return pollRunCompletion(threadId, run.id);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Prevention**: Implement fallback to polling if streaming fails.
|
||||
|
||||
**Source**: [OpenAI Community](https://community.openai.com/t/streaming-stopped-at-thread-run-requires-action-when-handling-openai-assistants-function-calling/943674)
|
||||
|
||||
---
|
||||
|
||||
## 9. Vector Store Quota Limits
|
||||
|
||||
**Error**: `429 Rate limit reached for vector store operations`.
|
||||
|
||||
**Cause**: Too many vector store operations or storage exceeded.
|
||||
|
||||
**Solution**:
|
||||
- **Monitor storage**: Check `usage_bytes` regularly
|
||||
- **Delete unused stores**: Clean up old vector stores
|
||||
- **Batch operations**: Use batch file uploads instead of individual uploads
|
||||
|
||||
```typescript
|
||||
async function cleanupOldVectorStores(keepDays = 30) {
|
||||
const stores = await openai.beta.vectorStores.list({ limit: 100 });
|
||||
|
||||
for (const store of stores.data) {
|
||||
const ageSeconds = Date.now() / 1000 - store.created_at;
|
||||
const ageDays = ageSeconds / (60 * 60 * 24);
|
||||
|
||||
if (ageDays > keepDays) {
|
||||
await openai.beta.vectorStores.del(store.id);
|
||||
console.log(`Deleted vector store: ${store.id}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Prevention**: Set auto-expiration on vector stores and monitor costs.
|
||||
|
||||
---
|
||||
|
||||
## 10. File Upload Format Incompatibility
|
||||
|
||||
**Error**: `400 Unsupported file type for this tool`.
|
||||
|
||||
**Cause**: Uploading file format not supported by the tool.
|
||||
|
||||
**Solution**:
|
||||
```typescript
|
||||
const SUPPORTED_FORMATS = {
|
||||
code_interpreter: [
|
||||
'.c', '.cpp', '.csv', '.docx', '.html', '.java', '.json', '.md',
|
||||
'.pdf', '.php', '.pptx', '.py', '.rb', '.tex', '.txt', '.css',
|
||||
'.jpeg', '.jpg', '.js', '.gif', '.png', '.tar', '.ts', '.xlsx', '.xml', '.zip'
|
||||
],
|
||||
file_search: [
|
||||
'.c', '.cpp', '.docx', '.html', '.java', '.json', '.md',
|
||||
'.pdf', '.php', '.pptx', '.py', '.rb', '.tex', '.txt', '.css', '.js', '.ts', '.go'
|
||||
],
|
||||
};
|
||||
|
||||
function validateFileFormat(filename: string, tool: 'code_interpreter' | 'file_search') {
|
||||
const ext = filename.substring(filename.lastIndexOf('.')).toLowerCase();
|
||||
|
||||
if (!SUPPORTED_FORMATS[tool].includes(ext)) {
|
||||
throw new Error(`Unsupported file format for ${tool}: ${ext}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Validate before upload
|
||||
validateFileFormat('data.csv', 'code_interpreter'); // OK
|
||||
validateFileFormat('video.mp4', 'file_search'); // Throws error
|
||||
```
|
||||
|
||||
**Prevention**: Validate file formats before uploading.
|
||||
|
||||
---
|
||||
|
||||
## 11. Assistant Instructions Token Limit
|
||||
|
||||
**Error**: `400 Instructions exceed maximum length`.
|
||||
|
||||
**Cause**: Instructions field exceeds 256,000 characters (v2 limit).
|
||||
|
||||
**Solution**:
|
||||
- **Use file search**: Put long instructions in documents
|
||||
- **Concise instructions**: Be clear and brief
|
||||
- **System messages**: Use thread-level messages for context
|
||||
|
||||
```typescript
|
||||
// ❌ Bad: Very long instructions
|
||||
const assistant = await openai.beta.assistants.create({
|
||||
instructions: "..." // 300k characters
|
||||
});
|
||||
|
||||
// ✅ Good: Concise instructions + file search
|
||||
const assistant = await openai.beta.assistants.create({
|
||||
instructions: "You are a support assistant. Use file_search to find answers in the knowledge base.",
|
||||
tools: [{ type: "file_search" }],
|
||||
tool_resources: {
|
||||
file_search: {
|
||||
vector_store_ids: [vectorStoreId], // Long content here
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
**Prevention**: Keep instructions under 256k characters; use file search for knowledge.
|
||||
|
||||
---
|
||||
|
||||
## 12. Thread Deletion While Run Active
|
||||
|
||||
**Error**: `400 Cannot delete thread while run is active`.
|
||||
|
||||
**Cause**: Attempting to delete a thread that has an active run.
|
||||
|
||||
**Solution**:
|
||||
```typescript
|
||||
async function safeDeleteThread(threadId: string) {
|
||||
// Cancel active runs first
|
||||
const runs = await openai.beta.threads.runs.list(threadId);
|
||||
|
||||
for (const run of runs.data) {
|
||||
if (['queued', 'in_progress'].includes(run.status)) {
|
||||
await openai.beta.threads.runs.cancel(threadId, run.id);
|
||||
|
||||
// Wait for cancellation
|
||||
let runStatus = run;
|
||||
while (runStatus.status !== 'cancelled') {
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
runStatus = await openai.beta.threads.runs.retrieve(threadId, run.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now safe to delete
|
||||
await openai.beta.threads.del(threadId);
|
||||
}
|
||||
```
|
||||
|
||||
**Prevention**: Cancel all active runs before deleting threads.
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Error | Quick Fix |
|
||||
|-------|-----------|
|
||||
| Thread has active run | Cancel or wait for run completion |
|
||||
| Polling timeout | Set timeout and cancel long runs |
|
||||
| Vector store not ready | Wait for `status: "completed"` |
|
||||
| File search no results | Check indexing complete, improve queries |
|
||||
| Code Interpreter file lost | Download immediately after run |
|
||||
| 100k message limit | Archive old threads, start new ones |
|
||||
| Function timeout | Add timeouts to function execution |
|
||||
| Stream interrupted | Fall back to polling |
|
||||
| Vector store quota | Clean up old stores, use batch uploads |
|
||||
| Unsupported file format | Validate file extensions before upload |
|
||||
| Instructions too long | Use file search for knowledge |
|
||||
| Can't delete thread | Cancel active runs first |
|
||||
|
||||
---
|
||||
|
||||
**Additional Resources**:
|
||||
- [OpenAI Assistants API Docs](https://platform.openai.com/docs/assistants)
|
||||
- [OpenAI Community Forum](https://community.openai.com/c/api/assistants-api/49)
|
||||
- [API Reference](https://platform.openai.com/docs/api-reference/assistants)
|
||||
422
references/vector-stores.md
Normal file
422
references/vector-stores.md
Normal file
@@ -0,0 +1,422 @@
|
||||
# Vector Stores - Complete Reference
|
||||
|
||||
In-depth guide to OpenAI's Vector Stores for the Assistants API.
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Vector Stores provide scalable semantic search infrastructure for the file_search tool:
|
||||
- **Capacity**: Up to 10,000 files per assistant
|
||||
- **Automatic**: Chunking, embedding, indexing
|
||||
- **Search**: Vector + keyword hybrid with reranking
|
||||
- **Pricing**: $0.10/GB/day (first 1GB free)
|
||||
|
||||
---
|
||||
|
||||
## Creating Vector Stores
|
||||
|
||||
### Basic Creation
|
||||
|
||||
```typescript
|
||||
const vectorStore = await openai.beta.vectorStores.create({
|
||||
name: "Company Knowledge Base",
|
||||
});
|
||||
```
|
||||
|
||||
### With Auto-Expiration
|
||||
|
||||
```typescript
|
||||
const vectorStore = await openai.beta.vectorStores.create({
|
||||
name: "Temporary KB",
|
||||
expires_after: {
|
||||
anchor: "last_active_at",
|
||||
days: 7,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
**Anchors**:
|
||||
- `last_active_at`: Expires N days after last use (recommended)
|
||||
- `created_at`: Expires N days after creation (not yet available)
|
||||
|
||||
### With Metadata
|
||||
|
||||
```typescript
|
||||
const vectorStore = await openai.beta.vectorStores.create({
|
||||
name: "Q4 2025 Documentation",
|
||||
metadata: {
|
||||
department: "sales",
|
||||
quarter: "Q4-2025",
|
||||
version: "1.0",
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Adding Files
|
||||
|
||||
### Single File Upload
|
||||
|
||||
```typescript
|
||||
// 1. Upload file to OpenAI
|
||||
const file = await openai.files.create({
|
||||
file: fs.createReadStream("document.pdf"),
|
||||
purpose: "assistants",
|
||||
});
|
||||
|
||||
// 2. Add to vector store
|
||||
await openai.beta.vectorStores.files.create(vectorStore.id, {
|
||||
file_id: file.id,
|
||||
});
|
||||
```
|
||||
|
||||
### Batch Upload (Recommended)
|
||||
|
||||
```typescript
|
||||
// Upload multiple files
|
||||
const files = await Promise.all([
|
||||
openai.files.create({ file: fs.createReadStream("doc1.pdf"), purpose: "assistants" }),
|
||||
openai.files.create({ file: fs.createReadStream("doc2.md"), purpose: "assistants" }),
|
||||
openai.files.create({ file: fs.createReadStream("doc3.docx"), purpose: "assistants" }),
|
||||
]);
|
||||
|
||||
// Batch add to vector store
|
||||
const batch = await openai.beta.vectorStores.fileBatches.create(vectorStore.id, {
|
||||
file_ids: files.map(f => f.id),
|
||||
});
|
||||
|
||||
// Monitor progress
|
||||
let batchStatus = batch;
|
||||
while (batchStatus.status === 'in_progress') {
|
||||
await new Promise(r => setTimeout(r, 1000));
|
||||
batchStatus = await openai.beta.vectorStores.fileBatches.retrieve(
|
||||
vectorStore.id,
|
||||
batch.id
|
||||
);
|
||||
console.log(`${batchStatus.file_counts.completed}/${batchStatus.file_counts.total}`);
|
||||
}
|
||||
```
|
||||
|
||||
**Benefits of Batch Upload**:
|
||||
- Faster processing (parallel indexing)
|
||||
- Single operation to track
|
||||
- Better error handling
|
||||
|
||||
---
|
||||
|
||||
## Vector Store States
|
||||
|
||||
| State | Description |
|
||||
|-------|-------------|
|
||||
| `in_progress` | Files being indexed |
|
||||
| `completed` | All files indexed successfully |
|
||||
| `failed` | Indexing failed |
|
||||
| `expired` | Auto-expiration triggered |
|
||||
|
||||
**Important**: Wait for `completed` before using with assistants.
|
||||
|
||||
---
|
||||
|
||||
## File Management
|
||||
|
||||
### List Files in Vector Store
|
||||
|
||||
```typescript
|
||||
const files = await openai.beta.vectorStores.files.list(vectorStore.id, {
|
||||
limit: 100,
|
||||
});
|
||||
|
||||
for (const file of files.data) {
|
||||
console.log(`${file.id}: ${file.status}`);
|
||||
}
|
||||
```
|
||||
|
||||
### Remove File from Vector Store
|
||||
|
||||
```typescript
|
||||
await openai.beta.vectorStores.files.del(vectorStore.id, fileId);
|
||||
```
|
||||
|
||||
**Note**: This removes the file from the vector store but doesn't delete the file from OpenAI's storage.
|
||||
|
||||
### Check File Status
|
||||
|
||||
```typescript
|
||||
const file = await openai.beta.vectorStores.files.retrieve(vectorStore.id, fileId);
|
||||
|
||||
console.log(file.status); // "in_progress", "completed", "failed"
|
||||
|
||||
if (file.status === 'failed') {
|
||||
console.error(file.last_error);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pricing & Cost Management
|
||||
|
||||
### Pricing Structure
|
||||
|
||||
- **Storage**: $0.10 per GB per day
|
||||
- **Free tier**: First 1GB
|
||||
- **Calculation**: Total vector store size (not original file size)
|
||||
|
||||
**Example Costs**:
|
||||
| Original Files | Vector Store Size | Daily Cost | Monthly Cost |
|
||||
|----------------|-------------------|------------|--------------|
|
||||
| 500 MB | 0.5 GB | $0.00 | $0.00 (free tier) |
|
||||
| 2 GB | 2 GB | $0.10 | $3.00 |
|
||||
| 10 GB | 10 GB | $0.90 | $27.00 |
|
||||
| 50 GB | 50 GB | $4.90 | $147.00 |
|
||||
|
||||
### Monitor Usage
|
||||
|
||||
```typescript
|
||||
const store = await openai.beta.vectorStores.retrieve(vectorStoreId);
|
||||
|
||||
const sizeGB = store.usage_bytes / (1024 * 1024 * 1024);
|
||||
const costPerDay = Math.max(0, (sizeGB - 1) * 0.10);
|
||||
const costPerMonth = costPerDay * 30;
|
||||
|
||||
console.log(`Storage: ${sizeGB.toFixed(2)} GB`);
|
||||
console.log(`Cost: $${costPerDay.toFixed(4)}/day ($${costPerMonth.toFixed(2)}/month)`);
|
||||
```
|
||||
|
||||
### Cost Optimization
|
||||
|
||||
**1. Auto-Expiration**:
|
||||
```typescript
|
||||
expires_after: {
|
||||
anchor: "last_active_at",
|
||||
days: 30,
|
||||
}
|
||||
```
|
||||
|
||||
**2. Regular Cleanup**:
|
||||
```typescript
|
||||
async function cleanupUnusedVectorStores() {
|
||||
const stores = await openai.beta.vectorStores.list({ limit: 100 });
|
||||
|
||||
for (const store of stores.data) {
|
||||
const ageDays = (Date.now() / 1000 - store.created_at) / (60 * 60 * 24);
|
||||
|
||||
if (ageDays > 90) { // 90 days old
|
||||
await openai.beta.vectorStores.del(store.id);
|
||||
console.log(`Deleted: ${store.name}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**3. Deduplicate Content**:
|
||||
- Remove duplicate files before upload
|
||||
- Combine similar documents
|
||||
- Archive old versions
|
||||
|
||||
---
|
||||
|
||||
## Using with Assistants
|
||||
|
||||
### Attach to Assistant
|
||||
|
||||
```typescript
|
||||
const assistant = await openai.beta.assistants.create({
|
||||
name: "Support Bot",
|
||||
tools: [{ type: "file_search" }],
|
||||
tool_resources: {
|
||||
file_search: {
|
||||
vector_store_ids: [vectorStore.id],
|
||||
},
|
||||
},
|
||||
model: "gpt-4o",
|
||||
});
|
||||
```
|
||||
|
||||
### Multiple Vector Stores
|
||||
|
||||
```typescript
|
||||
// Combine multiple knowledge bases
|
||||
tool_resources: {
|
||||
file_search: {
|
||||
vector_store_ids: [generalKBId, productDocsId, policyDocsId],
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
**Limit**: Maximum of 1 vector store per assistant in current API (subject to change).
|
||||
|
||||
---
|
||||
|
||||
## Advanced Operations
|
||||
|
||||
### Update Metadata
|
||||
|
||||
```typescript
|
||||
const updated = await openai.beta.vectorStores.update(vectorStoreId, {
|
||||
name: "Updated Name",
|
||||
metadata: {
|
||||
version: "2.0",
|
||||
last_updated: new Date().toISOString(),
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Retrieve Vector Store Details
|
||||
|
||||
```typescript
|
||||
const store = await openai.beta.vectorStores.retrieve(vectorStoreId);
|
||||
|
||||
console.log({
|
||||
id: store.id,
|
||||
name: store.name,
|
||||
status: store.status,
|
||||
usage_bytes: store.usage_bytes,
|
||||
file_counts: store.file_counts,
|
||||
created_at: new Date(store.created_at * 1000),
|
||||
expires_at: store.expires_at ? new Date(store.expires_at * 1000) : null,
|
||||
metadata: store.metadata,
|
||||
});
|
||||
```
|
||||
|
||||
### List All Vector Stores
|
||||
|
||||
```typescript
|
||||
const stores = await openai.beta.vectorStores.list({
|
||||
limit: 20,
|
||||
order: "desc",
|
||||
});
|
||||
|
||||
for (const store of stores.data) {
|
||||
console.log(`${store.name}: ${store.file_counts.completed} files`);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Pre-Process Documents
|
||||
|
||||
- Remove headers/footers
|
||||
- Clean formatting
|
||||
- Extract text from images (OCR separately)
|
||||
- Organize with clear structure
|
||||
|
||||
### 2. Monitor Indexing
|
||||
|
||||
```typescript
|
||||
async function waitForIndexing(vectorStoreId: string, batchId: string) {
|
||||
let batch;
|
||||
const startTime = Date.now();
|
||||
|
||||
do {
|
||||
batch = await openai.beta.vectorStores.fileBatches.retrieve(vectorStoreId, batchId);
|
||||
|
||||
if (batch.status === 'failed') {
|
||||
throw new Error('Batch indexing failed');
|
||||
}
|
||||
|
||||
console.log(`Progress: ${batch.file_counts.completed}/${batch.file_counts.total}`);
|
||||
|
||||
await new Promise(r => setTimeout(r, 2000));
|
||||
|
||||
// Timeout after 10 minutes
|
||||
if (Date.now() - startTime > 600000) {
|
||||
throw new Error('Indexing timeout');
|
||||
}
|
||||
} while (batch.status === 'in_progress');
|
||||
|
||||
return batch;
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Set Reasonable Expiration
|
||||
|
||||
```typescript
|
||||
// For temporary projects
|
||||
expires_after: { anchor: "last_active_at", days: 7 }
|
||||
|
||||
// For active knowledge bases
|
||||
expires_after: { anchor: "last_active_at", days: 90 }
|
||||
|
||||
// For permanent KB (no expiration)
|
||||
// Don't set expires_after
|
||||
```
|
||||
|
||||
### 4. Tag with Metadata
|
||||
|
||||
```typescript
|
||||
metadata: {
|
||||
project: "project-alpha",
|
||||
environment: "production",
|
||||
version: "1.0",
|
||||
owner: "team@company.com",
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Files Not Indexing
|
||||
|
||||
**Check file status**:
|
||||
```typescript
|
||||
const file = await openai.beta.vectorStores.files.retrieve(vectorStoreId, fileId);
|
||||
|
||||
if (file.status === 'failed') {
|
||||
console.error(file.last_error);
|
||||
}
|
||||
```
|
||||
|
||||
**Common causes**:
|
||||
- Unsupported file format
|
||||
- Corrupted file
|
||||
- File too large (>512 MB)
|
||||
|
||||
### Vector Store Shows `failed` Status
|
||||
|
||||
**Check batch details**:
|
||||
```typescript
|
||||
const batch = await openai.beta.vectorStores.fileBatches.retrieve(vectorStoreId, batchId);
|
||||
console.log(batch.file_counts); // Check failed count
|
||||
```
|
||||
|
||||
**Solutions**:
|
||||
- Remove failed files
|
||||
- Re-upload with correct format
|
||||
- Check error messages
|
||||
|
||||
### High Storage Costs
|
||||
|
||||
**Audit vector stores**:
|
||||
```typescript
|
||||
const stores = await openai.beta.vectorStores.list({ limit: 100 });
|
||||
let totalGB = 0;
|
||||
|
||||
for (const store of stores.data) {
|
||||
const sizeGB = store.usage_bytes / (1024 * 1024 * 1024);
|
||||
totalGB += sizeGB;
|
||||
console.log(`${store.name}: ${sizeGB.toFixed(2)} GB`);
|
||||
}
|
||||
|
||||
console.log(`Total: ${totalGB.toFixed(2)} GB = $${((totalGB - 1) * 0.10).toFixed(2)}/day`);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Limits
|
||||
|
||||
| Resource | Limit |
|
||||
|----------|-------|
|
||||
| Files per vector store | 10,000 |
|
||||
| Vector stores per account | Not documented |
|
||||
| File size | 512 MB |
|
||||
| Storage (billable) | Unlimited (pay per GB) |
|
||||
| Indexing time | Varies by size |
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: 2025-10-25
|
||||
Reference in New Issue
Block a user