revenium-middleware-perplexity 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. revenium_middleware_perplexity-0.1.0/LICENSE +22 -0
  2. revenium_middleware_perplexity-0.1.0/PKG-INFO +461 -0
  3. revenium_middleware_perplexity-0.1.0/README.md +415 -0
  4. revenium_middleware_perplexity-0.1.0/examples/README.md +146 -0
  5. revenium_middleware_perplexity-0.1.0/examples/__init__.py +2 -0
  6. revenium_middleware_perplexity-0.1.0/examples/basic.py +62 -0
  7. revenium_middleware_perplexity-0.1.0/examples/example_decorator.py +192 -0
  8. revenium_middleware_perplexity-0.1.0/examples/example_openai_sdk.py +49 -0
  9. revenium_middleware_perplexity-0.1.0/examples/example_perplexity_sdk.py +48 -0
  10. revenium_middleware_perplexity-0.1.0/examples/example_streaming_perplexity_sdk.py +52 -0
  11. revenium_middleware_perplexity-0.1.0/examples/example_tracing.py +298 -0
  12. revenium_middleware_perplexity-0.1.0/examples/getting_started.py +47 -0
  13. revenium_middleware_perplexity-0.1.0/examples/streaming.py +57 -0
  14. revenium_middleware_perplexity-0.1.0/examples/trace_visualization.py +82 -0
  15. revenium_middleware_perplexity-0.1.0/pyproject.toml +80 -0
  16. revenium_middleware_perplexity-0.1.0/revenium_middleware_perplexity/__init__.py +92 -0
  17. revenium_middleware_perplexity-0.1.0/revenium_middleware_perplexity/middleware.py +388 -0
  18. revenium_middleware_perplexity-0.1.0/revenium_middleware_perplexity/perplexity_sdk.py +265 -0
  19. revenium_middleware_perplexity-0.1.0/revenium_middleware_perplexity/provider.py +84 -0
  20. revenium_middleware_perplexity-0.1.0/revenium_middleware_perplexity/trace_fields.py +172 -0
  21. revenium_middleware_perplexity-0.1.0/revenium_middleware_perplexity.egg-info/PKG-INFO +461 -0
  22. revenium_middleware_perplexity-0.1.0/revenium_middleware_perplexity.egg-info/SOURCES.txt +28 -0
  23. revenium_middleware_perplexity-0.1.0/revenium_middleware_perplexity.egg-info/dependency_links.txt +1 -0
  24. revenium_middleware_perplexity-0.1.0/revenium_middleware_perplexity.egg-info/requires.txt +24 -0
  25. revenium_middleware_perplexity-0.1.0/revenium_middleware_perplexity.egg-info/top_level.txt +2 -0
  26. revenium_middleware_perplexity-0.1.0/setup.cfg +4 -0
  27. revenium_middleware_perplexity-0.1.0/tests/test_dual_sdk.py +166 -0
  28. revenium_middleware_perplexity-0.1.0/tests/test_middleware.py +110 -0
  29. revenium_middleware_perplexity-0.1.0/tests/test_provider.py +60 -0
  30. revenium_middleware_perplexity-0.1.0/tests/test_trace_fields.py +99 -0
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Revenium
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
@@ -0,0 +1,461 @@
1
+ Metadata-Version: 2.4
2
+ Name: revenium-middleware-perplexity
3
+ Version: 0.1.0
4
+ Summary: A Python library that meters Perplexity AI usage to Revenium.
5
+ Author-email: Revenium <info@revenium.io>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/revenium/revenium-middleware-perplexity-python
8
+ Project-URL: Bug Tracker, https://github.com/revenium/revenium-middleware-perplexity-python/issues
9
+ Project-URL: Documentation, https://github.com/revenium/revenium-middleware-perplexity-python/blob/main/README.md
10
+ Keywords: perplexity,middleware,logging,token-usage,metering,revenium,ai,llm
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.8
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: License :: OSI Approved :: MIT License
19
+ Classifier: Operating System :: OS Independent
20
+ Classifier: Intended Audience :: Developers
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Requires-Python: >=3.8
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: wrapt
26
+ Requires-Dist: revenium_middleware>=0.3.5
27
+ Requires-Dist: python-dotenv>=0.19.0
28
+ Provides-Extra: openai
29
+ Requires-Dist: openai>=1.0.0; extra == "openai"
30
+ Provides-Extra: perplexity
31
+ Requires-Dist: perplexityai>=0.1.0; extra == "perplexity"
32
+ Provides-Extra: all
33
+ Requires-Dist: openai>=1.0.0; extra == "all"
34
+ Requires-Dist: perplexityai>=0.1.0; extra == "all"
35
+ Provides-Extra: dev
36
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
37
+ Requires-Dist: pytest-cov; extra == "dev"
38
+ Requires-Dist: flake8; extra == "dev"
39
+ Requires-Dist: black; extra == "dev"
40
+ Requires-Dist: mypy; extra == "dev"
41
+ Requires-Dist: openai-responses>=0.12.0; extra == "dev"
42
+ Requires-Dist: freezegun; extra == "dev"
43
+ Requires-Dist: openai>=1.0.0; extra == "dev"
44
+ Requires-Dist: perplexityai>=0.1.0; extra == "dev"
45
+ Dynamic: license-file
46
+
47
+ # Revenium Middleware for Perplexity (Python)
48
+
49
+ A lightweight, production-ready middleware that adds **Revenium metering and tracking** to Perplexity AI API calls in Python.
50
+
51
+ [![Python](https://img.shields.io/badge/Python-3.8%2B-blue)](https://www.python.org/)
52
+ [![Documentation](https://img.shields.io/badge/docs-revenium.io-blue)](https://docs.revenium.io)
53
+ [![Website](https://img.shields.io/badge/website-revenium.ai-blue)](https://www.revenium.ai)
54
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
55
+
56
+ ## Features
57
+
58
+ - **Automatic Metering** - Tracks all API calls with detailed usage metrics
59
+ - **Streaming Support** - Full support for streaming responses
60
+ - **Dual SDK Support** - Works with both OpenAI SDK and native Perplexity SDK
61
+ - **Custom Metadata** - Add custom tracking metadata to any request
62
+ - **Trace Visualization** - Built-in support for distributed tracing
63
+ - **Production Ready** - Battle-tested and optimized for production use
64
+
65
+ ## Quick Start
66
+
67
+ ### Installation
68
+
69
+ Choose the installation that matches your SDK preference:
70
+
71
+ ```bash
72
+ # For OpenAI SDK users (using Perplexity via OpenAI client)
73
+ pip install revenium-middleware-perplexity[openai]
74
+
75
+ # For native Perplexity SDK users
76
+ pip install revenium-middleware-perplexity[perplexity]
77
+
78
+ # For both SDKs (recommended for flexibility)
79
+ pip install revenium-middleware-perplexity[all]
80
+ ```
81
+
82
+ ### Basic Usage
83
+
84
+ #### Option 1: Using OpenAI SDK
85
+
86
+ Perfect if you're already using OpenAI SDK or want maximum compatibility:
87
+
88
+ ```python
89
+ import os
90
+ from dotenv import load_dotenv
91
+ from openai import OpenAI
92
+
93
+ # Load environment variables
94
+ load_dotenv()
95
+
96
+ # Import Revenium middleware (this automatically patches OpenAI)
97
+ import revenium_middleware_perplexity
98
+
99
+ # Create OpenAI client with Perplexity base URL
100
+ client = OpenAI(
101
+ api_key=os.getenv("PERPLEXITY_API_KEY"),
102
+ base_url="https://api.perplexity.ai"
103
+ )
104
+
105
+ # Make a chat completion request
106
+ response = client.chat.completions.create(
107
+ model="sonar",
108
+ messages=[
109
+ {"role": "user", "content": "What is the capital of France?"}
110
+ ]
111
+ )
112
+
113
+ print(response.choices[0].message.content)
114
+ # Usage data automatically sent to Revenium!
115
+ ```
116
+
117
+ #### Option 2: Using Native Perplexity SDK
118
+
119
+ Perfect if you prefer the official Perplexity SDK:
120
+
121
+ ```python
122
+ import os
123
+ from dotenv import load_dotenv
124
+ from perplexity import Perplexity
125
+
126
+ # Load environment variables
127
+ load_dotenv()
128
+
129
+ # Import Revenium middleware (this automatically patches Perplexity)
130
+ import revenium_middleware_perplexity
131
+
132
+ # Create Perplexity client
133
+ client = Perplexity(
134
+ api_key=os.getenv("PERPLEXITY_API_KEY")
135
+ )
136
+
137
+ # Make a chat completion request
138
+ response = client.chat.completions.create(
139
+ model="sonar",
140
+ messages=[
141
+ {"role": "user", "content": "What is the capital of France?"}
142
+ ]
143
+ )
144
+
145
+ print(response.choices[0].message.content)
146
+ # Usage data automatically sent to Revenium!
147
+ ```
148
+
149
+ > **Note**: Both approaches work identically! The middleware automatically detects which SDK you're using and applies the appropriate wrapper.
150
+
151
+ ### Environment Variables
152
+
153
+ Create a `.env` file in your project root:
154
+
155
+ ```env
156
+ # Perplexity Configuration
157
+ PERPLEXITY_API_KEY=pplx_your_perplexity_api_key
158
+
159
+ # Revenium Configuration
160
+ REVENIUM_METERING_API_KEY=hak_your_revenium_api_key
161
+ REVENIUM_METERING_BASE_URL=https://api.revenium.ai
162
+
163
+ # Optional: Logging
164
+ REVENIUM_LOG_LEVEL=INFO
165
+ ```
166
+
167
+ ## What Gets Tracked
168
+
169
+ The middleware automatically captures comprehensive usage data:
170
+
171
+ ### Usage Metrics
172
+ - **Token Counts** - Input tokens, output tokens, total tokens
173
+ - **Model Information** - Model name, provider (Perplexity)
174
+ - **Request Timing** - Request duration, response time
175
+ - **Cost Calculation** - Estimated costs based on current pricing
176
+
177
+ ### Business Context (Optional)
178
+ - **User Tracking** - Subscriber ID, email, credentials
179
+ - **Organization Data** - Organization ID, subscription ID, product ID
180
+ - **Task Classification** - Task type, agent identifier, trace ID
181
+
182
+ ### Technical Details
183
+ - **API Endpoints** - Chat completions
184
+ - **Request Types** - Streaming vs non-streaming
185
+ - **Error Tracking** - Failed requests, error types
186
+ - **Environment Info** - Development vs production usage
187
+
188
+ ## Advanced Usage
189
+
190
+ ### Custom Metadata
191
+
192
+ Add business context to track usage by customer, product, or feature:
193
+
194
+ ```python
195
+ response = client.chat.completions.create(
196
+ model="sonar-pro",
197
+ messages=[{"role": "user", "content": "Hello!"}],
198
+ usage_metadata={
199
+ "organization_id": "org-123",
200
+ "product_id": "prod-456",
201
+ "subscriber": {
202
+ "id": "user-789",
203
+ "email": "user@example.com"
204
+ },
205
+ "task_type": "chat",
206
+ "trace_id": "trace-abc-123"
207
+ }
208
+ )
209
+ ```
210
+
211
+ ### Streaming Responses
212
+
213
+ The middleware automatically handles streaming:
214
+
215
+ ```python
216
+ stream = client.chat.completions.create(
217
+ model="sonar-pro",
218
+ messages=[{"role": "user", "content": "Write a poem"}],
219
+ stream=True,
220
+ usage_metadata={"task_type": "creative_writing"}
221
+ )
222
+
223
+ for chunk in stream:
224
+ if chunk.choices[0].delta.content:
225
+ print(chunk.choices[0].delta.content, end="", flush=True)
226
+
227
+ # Usage data automatically sent after stream completes!
228
+ ```
229
+
230
+ ## Decorator Support
231
+
232
+ The middleware provides powerful decorators for automatic metadata injection, eliminating the need to pass `usage_metadata` to every API call.
233
+
234
+ ### `@revenium_metadata`
235
+
236
+ Automatically injects metadata into all Perplexity API calls within a function:
237
+
238
+ ```python
239
+ from revenium_middleware_perplexity import revenium_metadata
240
+ from openai import OpenAI
241
+
242
+ client = OpenAI(
243
+ api_key="pplx_your_api_key",
244
+ base_url="https://api.perplexity.ai"
245
+ )
246
+
247
+ @revenium_metadata(
248
+ organization_id="acme-corp",
249
+ task_type="customer-support",
250
+ trace_id="session-12345"
251
+ )
252
+ def handle_customer_query(question: str) -> str:
253
+ """
254
+ All Perplexity calls within this function automatically include:
255
+ - organization_id: "acme-corp"
256
+ - task_type: "customer-support"
257
+ - trace_id: "session-12345"
258
+ """
259
+ response = client.chat.completions.create(
260
+ model="sonar",
261
+ messages=[{"role": "user", "content": question}]
262
+ )
263
+ return response.choices[0].message.content
264
+
265
+ # No need to pass usage_metadata to each call!
266
+ answer = handle_customer_query("How do I reset my password?")
267
+ ```
268
+
269
+ **Benefits:**
270
+ - ✅ **Cleaner Code** - No repetitive `usage_metadata` parameters
271
+ - ✅ **Automatic Injection** - Metadata applied to all calls in function scope
272
+ - ✅ **API-Level Override** - API-level metadata takes precedence when needed
273
+ - ✅ **Async Support** - Works with both sync and async functions
274
+ - ✅ **Thread-Safe** - Uses `contextvars` for proper isolation
275
+
276
+ ### `@revenium_meter` (Selective Metering)
277
+
278
+ Control which functions are metered when selective metering is enabled:
279
+
280
+ ```python
281
+ from revenium_middleware_perplexity import revenium_meter, revenium_metadata
282
+
283
+ # Set in .env file:
284
+ # REVENIUM_SELECTIVE_METERING=true
285
+
286
+ @revenium_meter()
287
+ @revenium_metadata(task_type="premium-feature")
288
+ def premium_feature(prompt: str):
289
+ # ✅ This WILL be metered (decorated with @revenium_meter)
290
+ response = client.chat.completions.create(
291
+ model="sonar",
292
+ messages=[{"role": "user", "content": prompt}]
293
+ )
294
+ return response.choices[0].message.content
295
+
296
+ def free_feature(prompt: str):
297
+ # ❌ This will NOT be metered (no @revenium_meter decorator)
298
+ response = client.chat.completions.create(
299
+ model="sonar",
300
+ messages=[{"role": "user", "content": prompt}]
301
+ )
302
+ return response.choices[0].message.content
303
+ ```
304
+
305
+ **Accepted values for `REVENIUM_SELECTIVE_METERING`:**
306
+ - `"true"`, `"1"`, `"yes"`, `"on"` (case-insensitive) → Selective metering enabled
307
+ - `"false"`, `"0"`, `"no"`, `"off"`, or unset → All calls metered (default)
308
+
309
+ ## Distributed Tracing
310
+
311
+ The middleware supports comprehensive distributed tracing through environment variables, allowing you to track requests across services and understand the context of API calls.
312
+
313
+ ### Trace Fields
314
+
315
+ Set these environment variables to enable distributed tracing:
316
+
317
+ ```env
318
+ # Deployment Context
319
+ REVENIUM_ENVIRONMENT=production # Environment (dev, staging, prod)
320
+ REVENIUM_REGION=us-east-1 # Cloud region
321
+
322
+ # Trace Identification
323
+ REVENIUM_TRACE_TYPE=customer-workflow # Categorical trace identifier
324
+ REVENIUM_TRACE_NAME=Customer Onboarding # Human-readable trace label
325
+
326
+ # Distributed Tracing
327
+ REVENIUM_PARENT_TRANSACTION_ID=trace-123 # Parent transaction ID
328
+ REVENIUM_TRANSACTION_NAME=analyze-doc # Operation name
329
+
330
+ # Retry Tracking
331
+ REVENIUM_RETRY_NUMBER=0 # Retry attempt (0 = first attempt)
332
+
333
+ # Credential Identification
334
+ REVENIUM_CREDENTIAL_ALIAS=Perplexity Prod Key # Human-readable credential name
335
+ ```
336
+
337
+ ### Distributed Tracing Example
338
+
339
+ Track a request flowing through multiple services:
340
+
341
+ ```python
342
+ import os
343
+ import uuid
344
+ from openai import OpenAI
345
+
346
+ client = OpenAI(
347
+ api_key="pplx_your_api_key",
348
+ base_url="https://api.perplexity.ai"
349
+ )
350
+
351
+ # Generate workflow trace ID
352
+ workflow_id = str(uuid.uuid4())
353
+
354
+ # Set trace context
355
+ os.environ["REVENIUM_ENVIRONMENT"] = "production"
356
+ os.environ["REVENIUM_TRACE_TYPE"] = "document-pipeline"
357
+ os.environ["REVENIUM_TRACE_NAME"] = "Document Processing"
358
+ os.environ["REVENIUM_PARENT_TRANSACTION_ID"] = workflow_id
359
+
360
+ # Step 1: Analysis Service
361
+ os.environ["REVENIUM_TRANSACTION_NAME"] = "analyze-document"
362
+ response1 = client.chat.completions.create(
363
+ model="sonar",
364
+ messages=[{"role": "user", "content": "Analyze document"}],
365
+ extra_body={"usage_metadata": {"service": "analyzer", "step": 1}}
366
+ )
367
+
368
+ # Step 2: Extraction Service
369
+ os.environ["REVENIUM_TRANSACTION_NAME"] = "extract-content"
370
+ response2 = client.chat.completions.create(
371
+ model="sonar",
372
+ messages=[{"role": "user", "content": "Extract content"}],
373
+ extra_body={"usage_metadata": {"service": "extractor", "step": 2}}
374
+ )
375
+
376
+ # All calls are linked by workflow_id in Revenium dashboard!
377
+ ```
378
+
379
+ ### Trace Field Reference
380
+
381
+ | Field | Environment Variable | Description | Example |
382
+ |-------|---------------------|-------------|---------|
383
+ | **Environment** | `REVENIUM_ENVIRONMENT` | Deployment environment | `production`, `staging`, `dev` |
384
+ | **Region** | `REVENIUM_REGION` | Cloud region | `us-east-1`, `eu-west-1` |
385
+ | **Trace Type** | `REVENIUM_TRACE_TYPE` | Categorical identifier | `customer-workflow`, `batch-job` |
386
+ | **Trace Name** | `REVENIUM_TRACE_NAME` | Human-readable label | `Customer Onboarding`, `Daily Report` |
387
+ | **Parent Transaction ID** | `REVENIUM_PARENT_TRANSACTION_ID` | Parent transaction for distributed tracing | `uuid-string` |
388
+ | **Transaction Name** | `REVENIUM_TRANSACTION_NAME` | Operation name | `analyze-document`, `generate-summary` |
389
+ | **Retry Number** | `REVENIUM_RETRY_NUMBER` | Retry attempt number | `0` (first), `1` (first retry) |
390
+ | **Credential Alias** | `REVENIUM_CREDENTIAL_ALIAS` | Human-readable credential name | `Perplexity Production Key` |
391
+
392
+ These fields are automatically included in all metering data and help you:
393
+ - 🔍 **Track requests** across multiple services
394
+ - 📊 **Analyze patterns** by environment, region, or workflow type
395
+ - 🔄 **Monitor retries** and failure rates
396
+ - 🎯 **Identify bottlenecks** in distributed systems
397
+
398
+ ## Examples
399
+
400
+ The package includes comprehensive examples in the [`examples/`](examples/) directory:
401
+
402
+ ### Basic Examples
403
+ - **`getting_started.py`** - Simplest usage example
404
+ - **`basic.py`** - Custom metadata example
405
+ - **`streaming.py`** - Streaming responses
406
+
407
+ ### Advanced Examples
408
+ - **`example_decorator.py`** - Using `@revenium_metadata` and `@revenium_meter` decorators
409
+ - **`example_tracing.py`** - Distributed tracing with trace fields
410
+ - **`trace_visualization.py`** - Trace visualization setup
411
+
412
+ Run any example:
413
+
414
+ ```bash
415
+ python examples/getting_started.py
416
+ python examples/example_decorator.py
417
+ python examples/example_tracing.py
418
+ ```
419
+
420
+ See [`examples/README.md`](examples/README.md) for detailed documentation.
421
+
422
+ ## How It Works
423
+
424
+ 1. **Import**: Import `revenium_middleware_perplexity` to automatically patch OpenAI
425
+ 2. **Use Normally**: Use the OpenAI client with Perplexity's base URL
426
+ 3. **Automatic Tracking**: All requests are automatically tracked
427
+ 4. **Async Metering**: Usage data is sent to Revenium in the background
428
+ 5. **Transparent**: Original responses are returned unchanged
429
+
430
+ The middleware never blocks your application - if Revenium tracking fails, your Perplexity requests continue normally.
431
+
432
+ ## Supported APIs
433
+
434
+ - Chat Completions API (`client.chat.completions.create()`)
435
+ - Streaming API (`client.chat.completions.create(stream=True)`)
436
+
437
+ ## Requirements
438
+
439
+ - Python 3.8+
440
+ - Revenium API key
441
+ - Perplexity API key
442
+
443
+ ## Documentation
444
+
445
+ For detailed documentation, visit [docs.revenium.io](https://docs.revenium.io)
446
+
447
+ ## License
448
+
449
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
450
+
451
+ ## Support
452
+
453
+ For issues, feature requests, or contributions:
454
+
455
+ - **GitHub Repository**: [revenium/revenium-middleware-perplexity-python](https://github.com/revenium/revenium-middleware-perplexity-python)
456
+ - **Issues**: [Report bugs or request features](https://github.com/revenium/revenium-middleware-perplexity-python/issues)
457
+ - **Documentation**: [docs.revenium.io](https://docs.revenium.io)
458
+
459
+ ---
460
+
461
+ **Built by Revenium**