openai-assistants
|
Works with
Claude CodeCursorCodex CLIGitHub CopilotGemini CLI
---
name: openai-assistants
description: |
license: MIT
---
# OpenAI Assistants API v2
**Status**: Production Ready (⚠️ Deprecated - Sunset August 26, 2026)
**Package**: openai@6.16.0
**Last Updated**: 2026-01-21
**v1 Deprecated**: December 18, 2024
**v2 Sunset**: August 26, 2026 (migrate to Responses API)
---
## ⚠️ Deprecation Notice
**OpenAI is deprecating Assistants API in favor of [Responses API](../openai-responses/SKILL.md).**
**Timeline**: v1 deprecated Dec 18, 2024 | v2 sunset August 26, 2026
**Use this skill if**: Maintaining legacy apps or migrating existing code (12-18 month window)
**Don't use if**: Starting new projects (use `openai-responses` skill instead)
**Migration**: See `references/migration-to-responses.md`
---
## Quick Start
```bash
npm install openai@6.16.0
```
```typescript
import OpenAI from 'openai';
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
// 1. Create assistant
const assistant = await openai.beta.assistants.create({
name: "Math Tutor",
instructions: "You are a math tutor. Use code interpreter for calculations.",
tools: [{ type: "code_interpreter" }],
model: "gpt-5",
});
// 2. Create thread
const thread = await openai.beta.threads.create();
// 3. Add message
await openai.beta.threads.messages.create(thread.id, {
role: "user",
content: "Solve: 3x + 11 = 14",
});
// 4. Run assistant
const run = await openai.beta.threads.runs.create(thread.id, {
assistant_id: assistant.id,
});
// 5. Poll for completion
let status = await openai.beta.threads.runs.retrieve(thread.id, run.id);
while (status.status !== 'completed') {
await new Promise(r => setTimeout(r, 1000));
status = await openai.beta.threads.runs.retrieve(thread.id, run.id);
}
// 6. Get response
const messages = await openai.beta.threads.messages.list(thread.id);
console.log(messages.data[0].content[0].text.value);
```
---
## Core Concepts
**Four Main Objects:**
1. **Assistants**: Configured AI with instructions (max 256k chars in v2, was 32k in v1), model, tools, metadata
2. **Threads**: Conversation containers with persistent message history (max 100k messages)
3. **Messages**: User/assistant messages with optional file attachments
4. **Runs**: Async execution with states (queued, in_progress, requires_action, completed, failed, expired)
---
## Key API Patterns
### Assistants
```typescript
const assistant = await openai.beta.assistants.create({
model: "gpt-5",
instructions: "System prompt (max 256k chars in v2)",
tools: [{ type: "code_interpreter" }, { type: "file_search" }],
tool_resources: { file_search: { vector_store_ids: ["vs_123"] } },
});
```
**Key Limits**: 256k instruction chars (v2), 128 tools max, 16 metadata pairs
### Threads & Messages
```typescript
// Create thread with messages
const thread = await openai.beta.threads.create({
messages: [{ role: "user", content: "Hello" }],
});
// Add message with attachments
await openai.beta.threads.messages.create(thread.id, {
role: "user",
content: "Analyze this",
attachments: [{ file_id: "file_123", tools: [{ type: "code_interpreter" }] }],
});
// List messages
const msgs = await openai.beta.threads.messages.list(thread.id);
```
**Key Limits**: 100k messages per thread
---
### Runs
```typescript
// Create run with optional overrides
const run = await openai.beta.threads.runs.create(thread.id, {
assistant_id: "asst_123",
additional_messages: [{ role: "user", content: "Question" }],
max_prompt_tokens: 1000,
max_completion_tokens: 500,
});
// Poll until complete
let status = await openai.beta.threads.runs.retrieve(thread.id, run.id);
while (['queued', 'in_progress'].includes(status.status)) {
await new Promise(r => setTimeout(r, 1000));
status = await openai.beta.threads.runs.retrieve(thread.id, run.id);
}
```
**Run States**: `queued` → `in_progress` → `requires_action` (function calling) / `completed` / `failed` / `cancelled` / `expired` (10 min max)
---
### Streaming
```typescript
const stream = await openai.beta.threads.runs.stream(thread.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 || '');
}
}
```
**Key Events**: `thread.run.created`, `thread.message.delta` (streaming content), `thread.run.step.delta` (tool progress), `thread.run.completed`, `thread.run.requires_action` (function calling)
---
## Tools
### Code Interpreter
Runs Python code in sandbox. Generates charts, processes files (CSV, JSON, PDF, images). Max 512MB per file.
```typescript
// Attach file to message
attachments: [{ file_id: "file_123", tools: [{ type: "code_interpreter" }] }]
// Access generated files
for (const content of message.content) {
if (content.type === 'image_file') {
const fileContent = await openai.files.content(content.image_file.file_id);
}
}
```
### File Search (RAG)
Semantic search with vector stores. **10,000 files max** (v2, was 20 in v1). **Pricing**: $0.10/GB/day (1GB free).
```typescript
// Create vector store
const vs = await openai.beta.vectorStores.create({ name: "Docs" });
await openai.beta.vectorStores.files.create(vs.id, { file_id: "file_123" });
// Wait for indexing
let store = await openai.beta.vectorStores.retrieve(vs.id);
while (store.status === 'in_progress') {
await new Promise(r => setTimeout(r, 2000));
store = await openai.beta.vectorStores.retrieve(vs.id);
}
// Use in assistant
tool_resources: { file_search: { vector_store_ids: [vs.id] } }
```
**⚠️ Wait for `status: 'completed'` before using**
### Function Calling
Submit tool outputs when run.status === 'requires_action':
```typescript
if (run.status === 'requires_action') {
const toolCalls = run.required_action.submit_tool_outputs.tool_calls;
const outputs = toolCalls.map(tc => ({
tool_call_id: tc.id,
output: JSON.stringify(yourFunction(JSON.parse(tc.function.arguments))),
}));
run = await openai.beta.threads.runs.submitToolOutputs(thread.id, run.id, {
tool_outputs: outputs,
});
}
```
## File 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 (512MB max)
**File Search**: .c, .cpp, .docx, .html, .java, .json, .md, .pdf, .php, .pptx, .py, .rb, .tex, .txt, .css, .js, .ts, .go (512MB max)
---
## Known Issues
**1. Thread Already Has Active Run**
```
Error: 400 Can't add messages to thread_xxx while a run run_xxx is active.
```
**Fix**: Cancel active run first: `await openai.beta.threads.runs.cancel(threadId, runId)`
**2. Run Polling Timeout / Incomplete Status**
```
Error: OpenAIError: Final run has not been received
```
**Why It Happens**: Long-running tasks may exceed polling windows or finish with `incomplete` status
**Prevention**: Handle incomplete runs gracefully
```typescript
try {
const stream = await openai.beta.threads.runs.stream(thread.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 || '');
}
}
} catch (error) {
if (error.message?.includes('Final run has not been received')) {
// Run ended with 'incomplete' status - thread can continue
const run = await openai.beta.threads.runs.retrieve(thread.id, runId);
if (run.status === 'incomplete') {
// Handle: prompt user to continue, reduce max_completion_tokens, etc.
}
}
}
```
**Source**: [GitHub Issues #945](https://github.com/openai/openai-node/issues/945), [#1306](https://github.com/openai/openai-node/issues/1306), [#1439](https://github.com/openai/openai-node/issues/1439)
**3. Vector Store Not Ready**
Using vector store before indexing completes.
**Fix**: Poll `vectorStores.retrieve()` until `status === 'completed'` (see File Search section)
**4. File Upload Format Issues**
Unsupported file formats cause silent failures.
**Fix**: Validate file extensions before upload (see File Formats section)
**5. Vector Store Upload Documentation Incorrect**
```
Error: No 'files' provided to process
```
**Why It Happens**: Official documentation shows incorrect usage of `uploadAndPoll`
**Prevention**: Wrap file streams in `{ files: [...] }` object
```typescript
// ✅ Correct
await openai.beta.vectorStores.fileBatches.uploadAndPoll(vectorStoreId, {
files: fileStreams
});
// ❌ Wrong (shown in official docs)
await openai.beta.vectorStores.fileBatches.uploadAndPoll(vectorStoreId, fileStreams);
```
**Source**: [GitHub Issue #1337](https://github.com/openai/openai-node/issues/1337)
**6. Reasoning Models Reject Temperature Parameter**
```
Error: Unsupported parameter: 'temperature' is not supported with this model
```
**Why It Happens**: When updating assistant to o3-mini/o1-preview/o1-mini, old temperature settings persist
**Prevention**: Explicitly set temperature to `null`
```typescript
await openai.beta.assistants.update(assistantId, {
model: 'o3-mini',
reasoning_effort: 'medium',
temperature: null, // ✅ Must explicitly clear
top_p: null
});
```
**Source**: [GitHub Issue #1318](https://github.com/openai/openai-node/issues/1318)
**7. uploadAndPoll Returns Vector Store ID Instead of Batch ID**
```
Error: Invalid 'batch_id': 'vs_...'. Expected an ID that begins with 'vsfb_'.
```
**Why It Happens**: `uploadAndPoll` returns vector store object instead of batch object
**Prevention**: Use alternative methods to get batch ID
```typescript
// Option 1: Use createAndPoll after separate upload
const batch = await openai.vectorStores.fileBatches.createAndPoll(
vectorStoreId,
{ file_ids: uploadedFileIds }
);
// Option 2: List batches to find correct ID
const batches = await openai.vectorStores.fileBatches.list(vectorStoreId);
const batchId = batches.data[0].id; // starts with 'vsfb_'
```
**Source**: [GitHub Issue #1700](https://github.com/openai/openai-node/issues/1700)
**8. Vector Store File Delete Affects All Stores**
**Warning**: Deleting a file from one vector store removes it from ALL vector stores
```typescript
// ❌ This deletes file from VS_A, VS_B, AND VS_C
await openai.vectorStores.files.delete('VS_A', 'file-xxx');
```
**Why It Happens**: SDK or API bug - delete operation has global effect
**Prevention**: Avoid sharing files across multiple vector stores if selective deletion is needed
**Source**: [GitHub Issue #1710](https://github.com/openai/openai-node/issues/1710)
**9. Memory Leak in Large File Uploads (Community-sourced)**
**Source**: [GitHub Issue #1052](https://github.com/openai/openai-node/issues/1052) | **Status**: OPEN
**Impact**: ~44MB leaked per 22MB file upload in long-running servers
**Why It Happens**: When uploading large files from streams (S3, etc.) using `vectorStores.fileBatches.uploadAndPoll`, memory may not be released after upload completes
**Verified**: Maintainer acknowledged, reduced in v4.58.1 but not eliminated
**Workaround**: Monitor memory usage in long-lived servers; restart periodically or use separate worker processes
**10. Thread Already Has Active Run - Race Condition (Community-sourced)**
**Enhancement to Issue #1**: When canceling an active run, race conditions may occur if the run completes before cancellation
```typescript
async function createRunSafely(threadId: string, assistantId: string) {
// Check for active runs first
const runs = await openai.beta.threads.runs.list(threadId, { limit: 1 });
const activeRun = runs.data.find(r =>
['queued', 'in_progress', 'requires_action'].includes(r.status)
);
if (activeRun) {
try {
await openai.beta.threads.runs.cancel(threadId, activeRun.id);
// Wait for cancellation to complete
let run = await openai.beta.threads.runs.retrieve(threadId, activeRun.id);
while (run.status === 'cancelling') {
await new Promise(r => setTimeout(r, 500));
run = await openai.beta.threads.runs.retrieve(threadId, activeRun.id);
}
} catch (error) {
// Ignore "already completed" errors - run finished naturally
if (!error.message?.includes('completed')) throw error;
}
}
return openai.beta.threads.runs.create(threadId, { assistant_id: assistantId });
}
```
**Source**: [OpenAI Community Forum](https://community.openai.com/t/error-running-thread-already-has-an-active-run/782118)
See `references/top-errors.md` for complete catalog.
## Relationship to Other Skills
**openai-api** (Chat Completions): Stateless, manual history, direct responses. Use for simple generation.
**openai-responses** (Responses API): ✅ **Recommended for new projects**. Better reasoning, modern MCP integration, active development.
**openai-assistants**: ⚠️ **Deprecated H1 2026**. Use for legacy apps only. Migration: `references/migration-to-responses.md`
---
## v1 to v2 Migration
**v1 deprecated**: Dec 18, 2024
**Key Changes**: `retrieval` → `file_search`, vector stores (10k files vs 20), 256k instructions (vs 32k), message-level file attachments
See `references/migration-from-v1.md`
---
**Templates**: `templates/basic-assistant.ts`, `code-interpreter-assistant.ts`, `file-search-assistant.ts`, `function-calling-assistant.ts`, `streaming-assistant.ts`
**References**: `references/top-errors.md`, `thread-lifecycle.md`, `vector-stores.md`, `migration-to-responses.md`, `migration-from-v1.md`
**Related Skills**: `openai-responses` (recommended), `openai-api`
---
**Last Updated**: 2026-01-21
**Package**: openai@6.16.0
**Status**: Production Ready (⚠️ Deprecated - Sunset August 26, 2026)
**Changes**: Added 6 new known issues (vector store upload bugs, o3-mini temperature, memory leak), enhanced streaming error handlingMore General & Other skills
find-skills
vercel-labs/skills
Helps users discover and install agent skills when they ask questions like "how do I do X", "find a skill for X", "is there a skill that can...", or express interest in extending capabilities. This skill should be used when the user is looking for functionality that might exist as an installable skill.
1.5M
grill-me
mattpocock/skills
A relentless interview to sharpen a plan or design.
972.7k
grill-with-docs
mattpocock/skills
A relentless interview to sharpen a plan or design, which also creates docs (ADR's and glossary) as we go.
828.8k

