@revenium/perplexity
Version:
NodeJS middleware for Perplexity AI API with Revenium metering
867 lines (648 loc) • 22.5 kB
Markdown
# Revenium Middleware for Perplexity
A lightweight, production-ready middleware that adds **Revenium metering and tracking** to Perplexity AI API calls.
[](https://www.npmjs.com/package/@revenium/perplexity)
[](https://opensource.org/licenses/MIT)
## Features
- ✅ **Zero Configuration** - Works out of the box with environment variables
- ✅ **Automatic Metering** - Tracks all API calls with detailed usage metrics
- ✅ **Streaming Support** - Full support for streaming responses
- ✅ **TypeScript First** - Built with TypeScript, includes full type definitions
- ✅ **Multi-Format** - Supports both ESM and CommonJS
- ✅ **Custom Metadata** - Add custom tracking metadata to any request
- ✅ **Production Ready** - Battle-tested and optimized for production use
## Package Migration
This package has been renamed from `revenium-middleware-perplexity-node` to `@revenium/perplexity` for better organization and simpler naming.
### Migration Steps
If you're upgrading from the old package:
```bash
# Uninstall the old package
npm uninstall revenium-middleware-perplexity-node
# Install the new package
npm install @revenium/perplexity
```
**Update your imports:**
```typescript
// Old import
import "revenium-middleware-perplexity-node";
// New import
import "@revenium/perplexity";
```
All functionality remains exactly the same - only the package name has changed.
## Table of Contents
- [Installation](https://github.com/revenium/revenium-middleware-perplexity-node#-installation)
- [Three Ways to Use This Middleware](https://github.com/revenium/revenium-middleware-perplexity-node#-three-ways-to-use-this-middleware)
- [Option 1: New Project with npm Package](https://github.com/revenium/revenium-middleware-perplexity-node#option-1-new-project-with-npm-package-recommended)
- [Option 2: Clone and Use Locally](https://github.com/revenium/revenium-middleware-perplexity-node#option-2-clone-and-use-locally)
- [Option 3: Add to Existing Project](https://github.com/revenium/revenium-middleware-perplexity-node#option-3-add-to-existing-project)
- [Quick Start](https://github.com/revenium/revenium-middleware-perplexity-node#-quick-start)
- [API Reference](https://github.com/revenium/revenium-middleware-perplexity-node#-api-reference)
- [Examples](https://github.com/revenium/revenium-middleware-perplexity-node#-examples)
- [Environment Variables](https://github.com/revenium/revenium-middleware-perplexity-node#-environment-variables)
## Installation
```bash
npm install @revenium/perplexity
```
## Three Ways to Use This Middleware
### Option 1: New Project with npm Package (Recommended)
**Best for:** Starting a new project or adding Perplexity with Revenium to an existing project.
#### Step 1: Create a new project
```bash
mkdir my-perplexity-project
cd my-perplexity-project
npm init -y
```
#### Step 2: Install the middleware
```bash
npm install @revenium/perplexity dotenv
```
#### Step 3: Create `.env` file
```env
# Perplexity API Configuration
PERPLEXITY_API_KEY=your_perplexity_api_key
# Revenium Metering Configuration
REVENIUM_METERING_API_KEY=your_revenium_api_key
REVENIUM_METERING_BASE_URL=https://api.revenium.io/meter
```
#### Step 4: Create `index.js`
```javascript
const {
initializeReveniumFromEnv,
initializePerplexityFromEnv,
createChatCompletion,
PERPLEXITY_MODELS,
} = require("@revenium/perplexity");
async function main() {
// Initialize configurations
initializeReveniumFromEnv();
initializePerplexityFromEnv();
// Create a chat completion
const result = await createChatCompletion({
messages: [{ role: "user", content: "What is the capital of France?" }],
model: PERPLEXITY_MODELS.SONAR_PRO,
});
console.log("Response:", result.content);
console.log("Tokens used:", result.usage.totalTokens);
}
main().catch(console.error);
```
#### Step 5: Run your project
```bash
node index.js
```
---
### Option 2: Clone and Use Locally
**Best for:** Development, testing, or contributing to the middleware.
#### Step 1: Clone the repository
```bash
git clone https://github.com/revenium/revenium-middleware-perplexity-node.git
cd revenium-middleware-perplexity-node
```
#### Step 2: Install dependencies
```bash
npm install
```
#### Step 3: Create `.env` file
```env
# Perplexity API Configuration
PERPLEXITY_API_KEY=your_perplexity_api_key
# Revenium Metering Configuration
REVENIUM_METERING_API_KEY=your_revenium_api_key
REVENIUM_METERING_BASE_URL=https://api.revenium.io/meter
```
#### Step 4: Build the project
```bash
npm run build
```
#### Step 5: Run examples
**TypeScript Examples:**
```bash
npm run example:basic # Basic chat completion
npm run example:streaming # Streaming response
npm run example:chat # Multi-turn conversation
npm run example:metadata # Custom metadata
```
**JavaScript Playground:**
```bash
npm run playground:basic # Basic chat completion
npm run playground:streaming # Streaming response
npm run playground:chat # Multi-turn conversation
npm run playground:metadata # Custom metadata
```
#### Step 6: Use in your own code
After building, you can import from the local build:
```javascript
const {
initializeReveniumFromEnv,
initializePerplexityFromEnv,
createChatCompletion,
} = require("./dist/cjs");
// Your code here...
```
---
### Option 3: Add to Existing Project
**Best for:** Integrating Perplexity with Revenium into an existing Node.js project.
#### Step 1: Install the middleware
```bash
npm install @revenium/perplexity
```
#### Step 2: Add environment variables
Add to your existing `.env` file:
```env
# Perplexity API Configuration
PERPLEXITY_API_KEY=your_perplexity_api_key
# Revenium Metering Configuration
REVENIUM_METERING_API_KEY=your_revenium_api_key
REVENIUM_METERING_BASE_URL=https://api.revenium.io/meter
```
#### Step 3: Initialize in your application
**For CommonJS projects:**
```javascript
require("dotenv").config();
const {
initializeReveniumFromEnv,
initializePerplexityFromEnv,
createChatCompletion,
PERPLEXITY_MODELS,
} = require("@revenium/perplexity");
// Initialize once at app startup
initializeReveniumFromEnv();
initializePerplexityFromEnv();
// Use anywhere in your app
async function askPerplexity(question) {
const result = await createChatCompletion({
messages: [{ role: "user", content: question }],
model: PERPLEXITY_MODELS.SONAR_PRO,
});
return result.content;
}
```
**For ES Modules projects:**
```javascript
import "dotenv/config";
import {
initializeReveniumFromEnv,
initializePerplexityFromEnv,
createChatCompletion,
PERPLEXITY_MODELS,
} from "@revenium/perplexity";
// Initialize once at app startup
initializeReveniumFromEnv();
initializePerplexityFromEnv();
// Use anywhere in your app
export async function askPerplexity(question) {
const result = await createChatCompletion({
messages: [{ role: "user", content: question }],
model: PERPLEXITY_MODELS.SONAR_PRO,
});
return result.content;
}
```
**For TypeScript projects:**
```typescript
import "dotenv/config";
import {
initializeReveniumFromEnv,
initializePerplexityFromEnv,
createChatCompletion,
PERPLEXITY_MODELS,
type ChatCompletionResult,
} from "@revenium/perplexity";
// Initialize once at app startup
initializeReveniumFromEnv();
initializePerplexityFromEnv();
// Use anywhere in your app
export async function askPerplexity(question: string): Promise<string> {
const result: ChatCompletionResult = await createChatCompletion({
messages: [{ role: "user", content: question }],
model: PERPLEXITY_MODELS.SONAR_PRO,
});
return result.content;
}
```
---
## Quick Start
### 1. Set up environment variables
Create a `.env` file in your project root:
```env
# Perplexity API Configuration
PERPLEXITY_API_KEY=your_perplexity_api_key
# Revenium Metering Configuration
REVENIUM_METERING_API_KEY=your_revenium_api_key
REVENIUM_METERING_BASE_URL=https://api.revenium.io/meter
```
### 2. Initialize and use
```typescript
import {
initializeReveniumFromEnv,
initializePerplexityFromEnv,
createChatCompletion,
PERPLEXITY_MODELS,
} from "@revenium/perplexity";
// Initialize configurations
initializeReveniumFromEnv();
initializePerplexityFromEnv();
// Create a chat completion
const result = await createChatCompletion({
messages: [{ role: "user", content: "What is the capital of France?" }],
model: PERPLEXITY_MODELS.SONAR_PRO,
});
console.log(result.content);
// Output: "The capital of France is Paris."
```
## API Reference
### Configuration
#### `initializeReveniumFromEnv()`
Initialize Revenium configuration from environment variables.
```typescript
const config = initializeReveniumFromEnv();
```
#### `initializePerplexityFromEnv()`
Initialize Perplexity configuration from environment variables.
```typescript
const config = initializePerplexityFromEnv();
```
#### `initializeRevenium(config)`
Initialize Revenium with custom configuration.
```typescript
initializeRevenium({
meteringApiKey: "your_api_key",
meteringBaseUrl: "https://api.revenium.io/meter",
teamId: "your_team_id", // Optional
});
```
#### `initializePerplexity(config)`
Initialize Perplexity with custom configuration.
```typescript
initializePerplexity({
apiKey: "your_perplexity_api_key",
baseUrl: "https://api.perplexity.ai", // Optional
});
```
### Chat Completions
#### `createChatCompletion(params)`
Create a chat completion with automatic metering.
```typescript
const result = await createChatCompletion({
messages: [
{ role: "system", content: "You are a helpful assistant." },
{ role: "user", content: "Hello!" },
],
model: PERPLEXITY_MODELS.SONAR_PRO,
maxTokens: 100,
temperature: 0.7,
usageMetadata: {
// Optional
subscriber: { id: "user-123" },
organizationId: "org-456",
productId: "product-789",
},
});
console.log(result.content);
console.log(result.usage);
console.log(result.transactionId);
```
**Parameters:**
- `messages` - Array of message objects with `role` and `content`
- `model` - Perplexity model to use (see [Available Models](https://github.com/revenium/revenium-middleware-perplexity-node#available-models))
- `maxTokens` - Maximum tokens to generate (optional)
- `temperature` - Sampling temperature 0-2 (optional)
- `topP` - Nucleus sampling parameter (optional)
- `presencePenalty` - Presence penalty -2 to 2 (optional)
- `frequencyPenalty` - Frequency penalty -2 to 2 (optional)
- `usageMetadata` - Custom metadata for tracking (optional)
**Returns:**
```typescript
{
content: string;
role: string;
finishReason: string | null;
usage: {
promptTokens: number;
completionTokens: number;
totalTokens: number;
}
model: string;
transactionId: string;
rawResponse: PerplexityResponse;
}
```
#### `createStreamingChatCompletion(params)`
Create a streaming chat completion.
```typescript
const result = await createStreamingChatCompletion({
messages: [{ role: "user", content: "Count from 1 to 5" }],
model: PERPLEXITY_MODELS.SONAR_PRO,
});
for await (const chunk of result.stream) {
const content = chunk.choices[0]?.delta?.content || "";
process.stdout.write(content);
}
```
**Returns:**
```typescript
{
stream: AsyncGenerator<PerplexityStreamChunk>;
transactionId: string;
model: string;
}
```
### Available Models
```typescript
import { PERPLEXITY_MODELS } from "@revenium/perplexity";
// Online Models (with internet access)
PERPLEXITY_MODELS.SONAR; // "sonar"
PERPLEXITY_MODELS.SONAR_PRO; // "sonar-pro"
PERPLEXITY_MODELS.SONAR_REASONING; // "sonar-reasoning"
// Chat Models (offline)
PERPLEXITY_MODELS.LLAMA_3_1_SONAR_SMALL_128K_CHAT; // "llama-3.1-sonar-small-128k-chat"
PERPLEXITY_MODELS.LLAMA_3_1_SONAR_LARGE_128K_CHAT; // "llama-3.1-sonar-large-128k-chat"
PERPLEXITY_MODELS.LLAMA_3_1_SONAR_HUGE_128K_CHAT; // "llama-3.1-sonar-huge-128k-chat"
```
### Utility Functions
#### `disableRevenium()` / `enableRevenium()`
Temporarily disable or enable Revenium metering.
```typescript
import { disableRevenium, enableRevenium } from "@revenium/perplexity";
disableRevenium(); // Stop sending metering data
// ... make API calls ...
enableRevenium(); // Resume sending metering data
```
#### `generateTransactionId()`
Generate a unique transaction ID.
```typescript
import { generateTransactionId } from "@revenium/perplexity";
const txnId = generateTransactionId();
```
## Examples
### Basic Chat Completion
```typescript
import {
initializeReveniumFromEnv,
initializePerplexityFromEnv,
createChatCompletion,
PERPLEXITY_MODELS,
} from "@revenium/perplexity";
initializeReveniumFromEnv();
initializePerplexityFromEnv();
const result = await createChatCompletion({
messages: [{ role: "user", content: "What is the capital of France?" }],
model: PERPLEXITY_MODELS.SONAR_PRO,
});
console.log(result.content);
```
### Streaming Response
```typescript
const result = await createStreamingChatCompletion({
messages: [{ role: "user", content: "Write a short poem about AI" }],
model: PERPLEXITY_MODELS.SONAR_PRO,
});
for await (const chunk of result.stream) {
const content = chunk.choices[0]?.delta?.content || "";
process.stdout.write(content);
}
```
### Multi-turn Conversation
```typescript
const messages = [
{ role: "system", content: "You are a helpful assistant." },
{ role: "user", content: "What is the capital of France?" },
];
const response1 = await createChatCompletion({
messages,
model: PERPLEXITY_MODELS.SONAR_PRO,
});
messages.push({ role: "assistant", content: response1.content });
messages.push({ role: "user", content: "What is its population?" });
const response2 = await createChatCompletion({
messages,
model: PERPLEXITY_MODELS.SONAR_PRO,
});
```
### Custom Metadata
```typescript
const result = await createChatCompletion({
messages: [{ role: "user", content: "Hello!" }],
model: PERPLEXITY_MODELS.SONAR_PRO,
usageMetadata: {
subscriber: {
id: "user-123",
email: "user@example.com",
},
organizationId: "org-456",
productId: "premium-plan",
traceId: "trace-abc-123",
},
});
```
## What Gets Tracked
The middleware automatically captures:
- **Token Usage**: Prompt and completion tokens for accurate billing
- **Request Duration**: Total time for each API call
- **Model Information**: Which Perplexity model was used
- **Operation Type**: Chat completion, streaming, etc.
- **Error Tracking**: Failed requests and error details
- **Streaming Metrics**: Time to first token for streaming responses
- **Custom Metadata**: Business context you provide
## Advanced Usage
### Streaming Responses
```typescript
import {
initializeReveniumFromEnv,
initializePerplexityFromEnv,
createStreamingChatCompletion,
PERPLEXITY_MODELS,
} from "@revenium/perplexity";
initializeReveniumFromEnv();
initializePerplexityFromEnv();
const result = await createStreamingChatCompletion({
messages: [{ role: "user", content: "Count from 1 to 5" }],
model: PERPLEXITY_MODELS.SONAR_PRO,
usageMetadata: {
subscriber: { id: "user-123" },
taskType: "counting-demo",
},
});
for await (const chunk of result.stream) {
const content = chunk.choices[0]?.delta?.content || "";
process.stdout.write(content);
}
```
### Custom Metadata Tracking
Add business context to your AI usage:
```typescript
const customMetadata = {
subscriber: {
id: "user-789",
email: "user@company.com",
credential: {
name: "premium-user",
value: "tier-1",
},
},
organizationId: "org-456",
productId: "premium-plan",
taskType: "RESEARCH",
agent: "ResearchBot",
traceId: "session-123",
responseQualityScore: 9.2,
};
const result = await createChatCompletion({
messages: [{ role: "user", content: "Research AI trends" }],
model: PERPLEXITY_MODELS.SONAR_PRO,
usageMetadata: customMetadata,
});
```
### Usage Metadata Interface
All metadata fields are optional:
```typescript
interface UsageMetadata {
traceId?: string; // Session or conversation ID
taskType?: string; // Type of AI task
organizationId?: string; // Organization/company ID
subscriptionId?: string; // Billing plan ID
productId?: string; // Your product/feature ID
agent?: string; // AI agent identifier
responseQualityScore?: number; // Quality score (0-1)
subscriber?: {
id?: string; // User ID from your system
email?: string; // User's email address
credential?: {
name?: string; // Credential name
value?: string; // Credential value
};
};
}
```
## Configuration Options
### Environment Variables
| Variable | Required | Default | Description |
| ---------------------------- | -------- | ------------------------------- | --------------------------------- |
| `PERPLEXITY_API_KEY` | Yes | - | Your Perplexity API key |
| `REVENIUM_METERING_API_KEY` | Yes | - | Your Revenium API key |
| `REVENIUM_METERING_BASE_URL` | Yes | - | Revenium metering API base URL |
| `PERPLEXITY_API_BASE_URL` | No | `https://api.perplexity.ai` | Perplexity API base URL |
| `DEBUG` | No | `false` | Enable debug logging |
### Manual Configuration
```typescript
import {
initializeRevenium,
initializePerplexity,
createChatCompletion,
} from "@revenium/perplexity";
// Manual configuration
initializeRevenium({
meteringApiKey: "hak_your_revenium_key",
meteringBaseUrl: "https://api.revenium.io/meter/v2",
});
initializePerplexity({
apiKey: "pplx_your_perplexity_key",
baseUrl: "https://api.perplexity.ai",
});
```
## Troubleshooting
### Common Issues
#### "Missing API Key" Error
```bash
# Make sure you've set the API keys
export PERPLEXITY_API_KEY="pplx_your_actual_api_key"
export REVENIUM_METERING_API_KEY="hak_your_actual_revenium_key"
# Verify they're set
echo $PERPLEXITY_API_KEY
echo $REVENIUM_METERING_API_KEY
```
#### "Requests not being tracked"
```bash
# Verify Revenium configuration
export REVENIUM_METERING_API_KEY="hak_your_actual_revenium_key"
export REVENIUM_METERING_BASE_URL="https://api.revenium.io/meter/v2"
# Enable debug logging to see what's happening
export DEBUG="true"
```
#### TypeScript errors with usageMetadata
- Ensure you're importing from `@revenium/perplexity`
- Check that your TypeScript version is 5.0+
- Verify you're using the latest version: `npm update @revenium/perplexity`
#### Build/Import Errors
```bash
# Clean and reinstall
rm -rf node_modules
npm install
# For TypeScript projects
npm run build
```
### Debug Mode
Enable debug logging to troubleshoot issues:
```bash
export DEBUG="true"
node your-script.js
```
This will show:
- Initialization details
- Configuration loading
- API call information
- Error details
## Project Structure
```
revenium-middleware-perplexity-node/
├── src/
│ ├── core/
│ │ ├── config/ # Configuration management
│ │ ├── tracking/ # Metering and tracking
│ │ └── wrapper/ # Perplexity API wrapper
│ ├── types/ # TypeScript type definitions
│ ├── utils/ # Utility functions
│ └── index.ts # Main entry point
├── examples/ # TypeScript examples
├── playground/ # JavaScript examples
└── dist/
├── cjs/ # CommonJS build
├── esm/ # ES Modules build
└── types/ # TypeScript definitions
```
## Running Examples
### TypeScript Examples
```bash
npm run example:basic # Basic chat completion
npm run example:streaming # Streaming response
npm run example:chat # Multi-turn conversation
npm run example:metadata # Custom metadata
```
### JavaScript Playground
```bash
npm run playground:basic # Basic chat completion
npm run playground:streaming # Streaming response
npm run playground:chat # Multi-turn conversation
npm run playground:metadata # Custom metadata
```
## How It Works
1. **Initialization**: When you call `initializePerplexityFromEnv()` and `initializeReveniumFromEnv()`, the middleware sets up configurations
2. **Request Wrapping**: All Perplexity API calls go through the middleware wrapper
3. **Usage Extraction**: Token counts, model info, and timing data are captured from responses
4. **Async Tracking**: Usage data is sent to Revenium in the background (fire-and-forget)
5. **Transparent Response**: Original Perplexity responses are returned unchanged
The middleware never blocks your application - if Revenium tracking fails, your Perplexity requests continue normally.
## Requirements
- Node.js 16+
- TypeScript 5.0+ (for TypeScript projects)
- Revenium API key
- Perplexity API key
## Documentation
For detailed documentation, visit [docs.revenium.io](https://docs.revenium.io)
## Contributing
See [CONTRIBUTING.md](https://github.com/revenium/revenium-middleware-perplexity-node/blob/HEAD/CONTRIBUTING.md)
## Code of Conduct
See [CODE_OF_CONDUCT.md](https://github.com/revenium/revenium-middleware-perplexity-node/blob/HEAD/CODE_OF_CONDUCT.md)
## Security
See [SECURITY.md](https://github.com/revenium/revenium-middleware-perplexity-node/blob/HEAD/SECURITY.md)
## License
This project is licensed under the MIT License - see the [LICENSE](https://github.com/revenium/revenium-middleware-perplexity-node/blob/HEAD/LICENSE) file for details.
## Support
For issues, feature requests, or contributions:
- **GitHub Repository**: [revenium/revenium-middleware-perplexity-node](https://github.com/revenium/revenium-middleware-perplexity-node)
- **Issues**: [Report bugs or request features](https://github.com/revenium/revenium-middleware-perplexity-node/issues)
- **Documentation**: [docs.revenium.io](https://docs.revenium.io)
- **Contact**: Reach out to the Revenium team for additional support
## Development
For development and testing instructions, see [DEVELOPMENT.md](https://github.com/revenium/revenium-middleware-perplexity-node/blob/HEAD/DEVELOPMENT.md).
---
**Built by Revenium**