SDK Quickstart — Agent Builders (OpenAI, LangChain, CrewAI)
Version: 1.0
Last Updated: 2026-04-19
Author: Milo (Backend Engineer — SDK & Developer Tools)
Status: Finalized — Ready for Aria (DevRel) review
Executive Summary
This document overhauls the BuyWhere developer quickstart for the three dominant AI agent frameworks. Goal: agent developer gets from zero to first product query in under 5 minutes, using their preferred framework.
Key changes from v0.1:
- Framework-first structure (OpenAI → LangChain → CrewAI), not language-first
- Complete tool-calling loop patterns for OpenAI
- Proper LangChain
Tool+Toolkitimplementations - First-ever CrewAI integration guide
- Unified 5-minute quickstart per framework
- API gap analysis with优先级
Quickstart Outline
Section 1 — 5-Minute Quickstart (Per Framework)
Each framework section follows: Install → API Key → First Query → Next Steps
- OpenAI Assistants API —
buywheretool definition + function-calling loop - LangChain —
BuyWhereToolas@tooldecorator + agent toolkit - CrewAI —
BuyWhereToolas CrewAIToolwith async execution
Section 2 — Core Patterns
- Auth + Basic Search (copy-paste ready)
- Compare + Best-Price Workflow
- Affiliate Link Generation
- Batch Search (parallel queries for agents)
Section 3 — Framework-Specific Gap Analysis
Identified gaps per framework with优先级 and Rex action items.
Section 4 — Implementation Recommendations
SDK package structure, versioning, and publish checklist.
Section 1: 5-Minute Quickstarts
OpenAI Assistants — 5 Minutes to First Query
Step 1: Install
npm install @buywhere/sdk @buywhere/openai openai
Step 2: Configure
import { createBuyWhereOpenAIAdapter, getAllTools } from '@buywhere/openai';
import OpenAI from 'openai';
const buywhere = createBuyWhereOpenAIAdapter({
apiKey: process.env.BUYWHERE_API_KEY!
});
const openai = new OpenAI();
const tools = getAllTools();
Step 3: Tool Executor
async function executeToolCall(toolName: string, args: Record<string, unknown>) {
const result = await buywhere.callTool(toolName, args);
if (!result || (Array.isArray(result) && result.length === 0)) {
return "No products found.";
}
const formatProduct = (p: any) =>
`- ${p.name}: ${p.currency} ${p.price} on ${p.merchant} [${p.rating ?? 'N/A'}★] (Buy: ${p.url})`;
if (Array.isArray(result) && result[0]?.products) {
return result[0].products.slice(0, 5).map(formatProduct).join('\n');
}
return JSON.stringify(result, null, 2);
}
Step 4: Assistant Loop
const assistant = await openai.beta.assistants.create({
name: "Shopping Agent",
instructions: "You help users find the best products and prices using BuyWhere search.",
tools: tools,
model: "gpt-4o"
});
const thread = await openai.beta.threads.create();
async function runAssistant(message: string) {
await openai.beta.threads.messages.create({
thread_id: thread.id,
role: "user",
content: message
});
let run = await openai.beta.threads.runs.createAndPoll({
thread_id: thread.id,
assistant_id: assistant.id
});
while (run.status === "requires_action") {
const toolCalls = run.required_action?.submit_tool_outputs?.tool_calls ?? [];
const toolOutputs: Array<{ tool_call_id: string; output: string }> = [];
for (const toolCall of toolCalls) {
const args = JSON.parse(toolCall.function.arguments);
const output = await executeToolCall(toolCall.function.name, args);
toolOutputs.push({ tool_call_id: toolCall.id, output });
}
run = await openai.beta.threads.runs.submitToolOutputsAndPoll({
thread_id: thread.id,
run_id: run.id,
tool_outputs: toolOutputs
});
}
return run;
}
// First query
const result = await runAssistant("Find me the best wireless headphones in Singapore under SGD 500");
LangChain — 5 Minutes to First Query
Step 1: Install
pip install buywhere-sdk langchain langchain-openai
Step 2: Define the Tool
from buywhere import BuyWhereClient
from langchain.tools import tool
import os
client = BuyWhereClient(api_key=os.environ["BUYWHERE_API_KEY"])
@tool
def search_products(query: str, country: str = "SG", limit: int = 5) -> str:
"""Search BuyWhere product catalog for products by query.
Args:
query: Natural language search query (e.g., 'sony wh-1000xm5')
country: ISO country code (SG, MY, PH, US, VN, TH)
limit: Number of results to return (1-100)
Returns:
Formatted list of products with name, price, merchant, rating, and buy link.
"""
results = client.search(query=query, country=country, limit=limit)
if not results["items"]:
return "No products found for this query."
lines = [f"Found {results['total']} products. Top results:"]
for p in results["items"][:limit]:
lines.append(
f"- {p['name']}: {p['currency']} {p['price']} "
f"on {p['source']} ★{p.get('rating', 'N/A')} "
f"[Buy]({p.get('affiliate_url', p.get('buy_url', ''))})"
)
return "\n".join(lines)
@tool
def compare_prices(product_ids: list[int]) -> str:
"""Compare product prices across multiple platforms.
Args:
product_ids: List of BuyWhere product IDs (max 10)
Returns:
Formatted comparison showing best price per product.
"""
comparison = client.compare(product_ids)
lines = []
for c in comparison["comparisons"]:
best = c.get("best_price")
if best:
lines.append(
f"- {c['product_name']}: Best price "
f"{best['currency']} {best['price']} on {best['source']} "
f"[Buy]({best.get('affiliate_url', best.get('buy_url', ''))})"
)
else:
lines.append(f"- {c['product_name']}: No prices found.")
return "\n".join(lines) if lines else "No comparison data available."
Step 3: Bind to LLM
from langchain_openai import ChatOpenAI
from langchain.agents import initialize_agent, AgentType
llm = ChatOpenAI(model="gpt-4o")
tools = [search_products, compare_prices]
agent = initialize_agent(
tools=tools,
llm=llm,
agent=AgentType.OPENAI_FUNCTIONS,
verbose=True
)
# First query
agent.run("Find me wireless headphones in Singapore and compare prices if there are multiple options")
LangChain Toolkit (recommended for production):
# For agents that need multiple BuyWhere tools
class BuyWhereToolkit:
def __init__(self, api_key: str):
self.client = BuyWhereClient(api_key=api_key)
def get_tools(self):
return [
search_products,
compare_prices,
# Add more tools as needed
]
CrewAI — 5 Minutes to First Query
Step 1: Install
pip install buywhere-sdk crewai crewai-tools
Step 2: Define Tools
from crewai_tools import BaseTool
from buywhere import BuyWhereClient
from pydantic import Field
import os
client = BuyWhereClient(api_key=os.environ["BUYWHERE_API_KEY"])
class BuyWhereSearchTool(BaseTool):
name: str = "buywhere_search"
description: str = "Search BuyWhere product catalog. Input should be a dict with 'query' (required), 'country' (optional, default SG), 'limit' (optional, default 5)."
def _run(self, query: str, country: str = "SG", limit: int = 5) -> str:
results = client.search(query=query, country=country, limit=limit)
if not results["items"]:
return "No products found."
lines = []
for p in results["items"][:limit]:
lines.append(
f"- {p['name']}: {p['currency']} {p['price']} "
f"on {p['source']} ★{p.get('rating', 'N/A')}"
)
return "\n".join(lines)
class BuyWhereCompareTool(BaseTool):
name: str = "buywhere_compare"
description: str = "Compare product prices across platforms. Input should be a dict with 'product_ids' (list of BuyWhere product IDs)."
def _run(self, product_ids: list) -> str:
comparison = client.compare(product_ids)
lines = []
for c in comparison["comparisons"]:
best = c.get("best_price")
if best:
lines.append(
f"- {c['product_name']}: {best['currency']} {best['price']} on {best['source']}"
)
return "\n".join(lines) if lines else "No comparison data."
search_tool = BuyWhereSearchTool()
compare_tool = BuyWhereCompareTool()
Step 3: Create Agents
from crewai import Agent, Task, Crew
researcher = Agent(
role="Product Researcher",
goal="Find the best products based on user requirements",
backstory="Expert at finding and comparing products using BuyWhere",
tools=[search_tool],
verbose=True
)
comparer = Agent(
role="Price Comparer",
goal="Compare product prices across platforms to find the best deal",
backstory="Expert at analyzing prices and finding the best value",
tools=[compare_tool],
verbose=True
)
Step 4: Create Tasks and Crew
task1 = Task(
description="Search for {product_query} in {country}",
agent=researcher,
expected_output="List of top 5 products with name, price, merchant, and rating"
)
task2 = Task(
description="Compare prices for the top products found",
agent=comparer,
expected_output="Best price recommendation with platform comparison"
)
crew = Crew(
agents=[researcher, comparer],
tasks=[task1, task2],
verbose=True
)
result = crew.kickoff(inputs={"product_query": "wireless headphones", "country": "SG"})
Section 2: Core Patterns
Pattern 1: Minimal Search (5 lines)
Python (httpx direct):
import httpx
response = httpx.get(
"https://api.buywhere.ai/v1/products/search",
params={"q": "wireless headphones", "country": "SG", "limit": 5},
headers={"Authorization": f"Bearer {api_key}"}
)
for p in response.json()["items"]:
print(f"{p['name']} — {p['currency']} {p['price']}")
TypeScript (fetch):
const res = await fetch("https://api.buywhere.ai/v1/products/search?q=wireless+headphones&country=SG&limit=5", {
headers: { Authorization: `Bearer ${apiKey}` }
});
const { items } = await res.json();
items.forEach(p => console.log(`${p.name} — ${p.currency} ${p.price}`));
Pattern 2: Compare + Best Deal
# Search → narrow to top 5 → batch compare → surface best deal
results = client.search("sony wh-1000xm5", country="SG", limit=10)
top5_ids = [p["id"] for p in results["items"][:5]]
comparison = client.compare(top5_ids)
for c in comparison["comparisons"]:
best = c.get("best_price")
if best:
print(f"{c['product_name']}: {best['currency']} {best['price']} on {best['source']}")
Pattern 3: Affiliate Link for Monetization
results = client.search("iphone 15 pro 256gb", country="SG", limit=3)
top = min(results["items"], key=lambda p: float(p["price"]))
recommendation = {
"product": top["name"],
"price": f"{top['currency']} {top['price']}",
"buy_link": top.get("affiliate_url", top.get("buy_url")), # Use for monetization
"merchant": top["source"],
"rating": top.get("rating"),
"in_stock": top.get("in_stock")
}
Section 3: Framework-Specific Gap Analysis
OpenAI Assistants API
| Gap | Description | Priority | Rex Action |
|---|---|---|---|
| No streaming support in tool loop | Real-time token display unavailable during tool execution | Medium | Add streaming callback support to tool executor pattern |
| No vision/image input | Product images not processed | Low | Document image_url field usage separately |
| Rate limit handling | No built-in retry with backoff in tool executor | High | Add @retry decorator to execute_buywhere_search |
LangChain
| Gap | Description | Priority | Rex Action |
|---|---|---|---|
| No official BuyWhere Toolkit | Developers must copy-paste tool definitions | High | Publish buywhere-langchain package |
| Async support | @tool decorator is sync-only; async agents need AsyncTool | Medium | Add BuyWhereAsyncTool base class |
| Structured output | compare returns nested dicts; LangChain prefers Pydantic | Medium | Add Pydantic models to SDK |
CrewAI
| Gap | Description | Priority | Rex Action |
|---|---|---|---|
| No BuyWhere Tools library | First-ever integration guide needed | High | Create and publish buywhere-crewai-tools |
BaseTool inheritance complexity | CrewAI tools require specific signature | Medium | Provide working BaseTool subclass examples |
| Parallel task execution | No pattern for agents working simultaneously | Medium | Document task dependency graph |
Section 4: API Gaps (Cross-Framework)
Critical (Blocks Beta Launch)
-
Batch search endpoint missing — Agents often need 3-5 queries in parallel. No
/v1/products/batch-search.- Workaround: Use
asyncio.gatherwith multipleclient.search()calls - Fix: Rex to add batch endpoint
- Workaround: Use
-
Path inconsistency for best-price — Blog uses
/v2/agents/best-price, docs showGET /v1/products/best-price.- Workaround: Use
/v1/products/best-price(documented) - Fix: Rex to align v2 path or deprecate
- Workaround: Use
High (SDK Beta)
-
UTM/tracking parameter append — Cannot append custom UTM to
affiliate_url.- Workaround: None (affiliate tracking not customizable)
- Fix: Rex to add
?utm_*passthrough to affiliate endpoint
-
POST /v1/products/compare/diffundocumented — Onlycompareis in main docs;diffonly in TypeScript types.- Fix: Rex to document both
compareandcompare/diff
- Fix: Rex to document both
Medium (SDK v0.2)
-
Price history endpoint — Agents cannot say "price at 90-day low".
- Fix: Rex to implement
/v1/products/{id}/price-history
- Fix: Rex to implement
-
Stock granularity —
in_stock: booleanbut agents want low/medium/high urgency.- Fix: Rex to add
stock_levelfield
- Fix: Rex to add
Low (SDK v1.0)
- Webhooks — Docs show "Coming Soon" but no ETA.
- Fix: Rex to provide timeline
Section 5: Implementation Recommendations
SDK Package Structure
buywhere-sdk/
├── src/
│ ├── client.py # BuyWhereClient (httpx-based)
│ ├── tools/
│ │ ├── __init__.py
│ │ ├── openai_tools.py # OpenAI function calling adapters
│ │ ├── langchain_tools.py # LangChain @tool decorators
│ │ └── crewai_tools.py # CrewAI BaseTool subclasses
│ └── models.py # Pydantic models for typed responses
├── package.json # For npm variant
└── pyproject.toml
buywhere-mcp/
├── src/
│ └── index.ts # MCP server implementation
├── package.json
└── smithery.yaml
Publish Checklist (for Rex/Board)
-
buywhere-sdkPython package to PyPI -
buywhere-sdkTypeScript package to npm (buywhereor@buywhere/sdk) -
buywhere-mcpto npm (blocked: needs npm automation token with 2FA bypass — see BUY-2433) -
buywhere-langchainpackage (future) -
buywhere-crewai-toolspackage (future)
Documentation Priority
- Now (Day 45): This quickstart doc + existing
docs/index.md - Day 50: Framework-specific integration guides (OpenAI, LangChain, CrewAI)
- Day 60: API reference with TypeScript types fully populated
Prepared by Milo — Backend Engineer (SDK & Developer Tools)
Review: Rex (API alignment), Aria (DevRel content review)