@revenium/openai 1.0.9 → 1.0.11
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.
- package/CHANGELOG.md +78 -0
- package/LICENSE +21 -21
- package/README.md +1231 -1152
- package/dist/cjs/index.js +2 -2
- package/dist/cjs/types/openai-augmentation.js +1 -1
- package/dist/esm/index.js +2 -2
- package/dist/esm/types/openai-augmentation.js +1 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/types/openai-augmentation.d.ts +1 -1
- package/examples/README.md +361 -0
- package/examples/azure-basic.ts +194 -0
- package/examples/azure-responses-basic.ts +204 -0
- package/examples/azure-responses-streaming.ts +226 -0
- package/examples/azure-streaming.ts +188 -0
- package/examples/openai-basic.ts +125 -0
- package/examples/openai-responses-basic.ts +183 -0
- package/examples/openai-responses-streaming.ts +203 -0
- package/examples/openai-streaming.ts +161 -0
- package/package.json +87 -84
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI Basic Example
|
|
3
|
+
*
|
|
4
|
+
* Shows how to use Revenium middleware with OpenAI chat completions and embeddings.
|
|
5
|
+
* Demonstrates seamless metadata integration - all metadata fields are optional!
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import 'dotenv/config';
|
|
9
|
+
import { initializeReveniumFromEnv, patchOpenAIInstance } from '@revenium/openai';
|
|
10
|
+
import OpenAI from 'openai';
|
|
11
|
+
|
|
12
|
+
async function openaiBasicExample() {
|
|
13
|
+
console.log(' OpenAI Basic Usage with Seamless Metadata Integration\n');
|
|
14
|
+
|
|
15
|
+
// Initialize Revenium middleware
|
|
16
|
+
const initResult = initializeReveniumFromEnv();
|
|
17
|
+
if (!initResult.success) {
|
|
18
|
+
console.error(' Failed to initialize Revenium:', initResult.message);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Create and patch OpenAI instance
|
|
23
|
+
const openai = patchOpenAIInstance(new OpenAI());
|
|
24
|
+
|
|
25
|
+
// Example 1: Basic chat completion (no metadata)
|
|
26
|
+
console.log('Example 1: Basic chat completion (automatic tracking)');
|
|
27
|
+
|
|
28
|
+
const basicResponse = await openai.chat.completions.create({
|
|
29
|
+
model: 'gpt-4o-mini',
|
|
30
|
+
messages: [{ role: 'user', content: 'What is TypeScript in one sentence?' }],
|
|
31
|
+
// No usageMetadata - still automatically tracked!
|
|
32
|
+
// No max_tokens - let response complete naturally
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
console.log('Response:', basicResponse.choices[0]?.message?.content);
|
|
36
|
+
console.log('Usage:', basicResponse.usage);
|
|
37
|
+
console.log(' Automatically tracked to Revenium without metadata\n');
|
|
38
|
+
|
|
39
|
+
// Example 2: Chat completion with rich metadata (all optional!)
|
|
40
|
+
console.log('Example 2: Chat completion with rich metadata');
|
|
41
|
+
|
|
42
|
+
const metadataResponse = await openai.chat.completions.create({
|
|
43
|
+
model: 'gpt-4o-mini',
|
|
44
|
+
messages: [
|
|
45
|
+
{ role: 'user', content: 'Explain the benefits of using middleware in 2 sentences.' },
|
|
46
|
+
],
|
|
47
|
+
|
|
48
|
+
// All metadata fields are optional - add what you need!
|
|
49
|
+
// Note: Nested subscriber structure matches Python middleware for consistency
|
|
50
|
+
usageMetadata: {
|
|
51
|
+
// User tracking (optional) - nested subscriber object
|
|
52
|
+
subscriber: {
|
|
53
|
+
id: 'user-12345',
|
|
54
|
+
email: 'developer@company.com',
|
|
55
|
+
credential: {
|
|
56
|
+
name: 'api-key',
|
|
57
|
+
value: 'key123',
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
// Business context (optional)
|
|
62
|
+
organizationId: 'my-customer',
|
|
63
|
+
productId: 'ai-assistant',
|
|
64
|
+
|
|
65
|
+
// Task classification (optional)
|
|
66
|
+
taskType: 'explanation-request',
|
|
67
|
+
traceId: `session-${Date.now()}`,
|
|
68
|
+
|
|
69
|
+
// Custom fields (optional)
|
|
70
|
+
agent: 'openai-basic-chat-node',
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
console.log('Response:', metadataResponse.choices[0]?.message?.content);
|
|
75
|
+
console.log('Usage:', metadataResponse.usage);
|
|
76
|
+
console.log(' Tracked with rich metadata for analytics\n');
|
|
77
|
+
|
|
78
|
+
// Example 3: Basic embeddings (no metadata)
|
|
79
|
+
console.log('Example 3: Basic embeddings (automatic tracking)');
|
|
80
|
+
|
|
81
|
+
const basicEmbedding = await openai.embeddings.create({
|
|
82
|
+
model: 'text-embedding-3-small',
|
|
83
|
+
input: 'Revenium middleware automatically tracks OpenAI usage',
|
|
84
|
+
// No usageMetadata - still automatically tracked!
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
console.log('Model:', basicEmbedding.model);
|
|
88
|
+
console.log('Usage:', basicEmbedding.usage);
|
|
89
|
+
console.log('Embedding dimensions:', basicEmbedding.data[0]?.embedding.length);
|
|
90
|
+
console.log('Embeddings automatically tracked without metadata\n');
|
|
91
|
+
|
|
92
|
+
// Example 4: Embeddings with metadata (all optional!)
|
|
93
|
+
console.log(' Example 4: Embeddings with rich metadata');
|
|
94
|
+
|
|
95
|
+
const metadataEmbedding = await openai.embeddings.create({
|
|
96
|
+
model: 'text-embedding-3-small',
|
|
97
|
+
input: 'Advanced text embedding with comprehensive tracking metadata',
|
|
98
|
+
|
|
99
|
+
// All metadata fields are optional - customize for your use case!
|
|
100
|
+
// Note: Nested subscriber structure for consistency across language implementations
|
|
101
|
+
usageMetadata: {
|
|
102
|
+
subscriber: {
|
|
103
|
+
id: 'embedding-user-789',
|
|
104
|
+
email: 'embeddings@company.com',
|
|
105
|
+
credential: {
|
|
106
|
+
name: 'embed-key',
|
|
107
|
+
value: 'embed123',
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
organizationId: 'my-company',
|
|
111
|
+
taskType: 'document-embedding',
|
|
112
|
+
productId: 'search-engine',
|
|
113
|
+
traceId: `embed-${Date.now()}`,
|
|
114
|
+
agent: 'openai-basic-embeddings-node',
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
console.log('Model:', metadataEmbedding.model);
|
|
119
|
+
console.log('Usage:', metadataEmbedding.usage);
|
|
120
|
+
console.log('Embedding dimensions:', metadataEmbedding.data[0]?.embedding.length);
|
|
121
|
+
console.log(' Embeddings tracked with metadata for business analytics\n');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Run the example
|
|
125
|
+
openaiBasicExample().catch(console.error);
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI Responses API Basic Examples
|
|
3
|
+
*
|
|
4
|
+
* This file demonstrates how to use the new OpenAI Responses API with the Revenium middleware.
|
|
5
|
+
* The Responses API is a new stateful API that brings together capabilities from chat completions
|
|
6
|
+
* and assistants API in one unified experience.
|
|
7
|
+
*
|
|
8
|
+
* Reference: https://learn.microsoft.com/en-us/azure/ai-foundry/openai/how-to/responses
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import 'dotenv/config';
|
|
12
|
+
import { initializeReveniumFromEnv, patchOpenAIInstance } from '@revenium/openai';
|
|
13
|
+
import OpenAI from 'openai';
|
|
14
|
+
|
|
15
|
+
// Import types for the new Responses API
|
|
16
|
+
import type { ResponsesCreateParams, ResponsesResponse } from '../src/types/responses-api.js';
|
|
17
|
+
|
|
18
|
+
async function main() {
|
|
19
|
+
// Initialize Revenium middleware
|
|
20
|
+
await initializeReveniumFromEnv();
|
|
21
|
+
|
|
22
|
+
// Create OpenAI client
|
|
23
|
+
const openai = new OpenAI({
|
|
24
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Patch the OpenAI instance to add Revenium tracking
|
|
28
|
+
patchOpenAIInstance(openai);
|
|
29
|
+
|
|
30
|
+
console.log(' OpenAI Responses API Basic Examples\n');
|
|
31
|
+
|
|
32
|
+
// Example 1: Basic Responses API call (no metadata)
|
|
33
|
+
console.log(' Example 1: Basic Responses API call (no metadata)');
|
|
34
|
+
try {
|
|
35
|
+
const responsesAPI = openai as any; // Type assertion for new API
|
|
36
|
+
|
|
37
|
+
if (responsesAPI.responses?.create) {
|
|
38
|
+
const response: ResponsesResponse = await responsesAPI.responses.create({
|
|
39
|
+
model: 'gpt-5',
|
|
40
|
+
input: 'What is the capital of France?',
|
|
41
|
+
} as ResponsesCreateParams);
|
|
42
|
+
|
|
43
|
+
console.log('Response ID:', response.id);
|
|
44
|
+
console.log('Model:', response.model);
|
|
45
|
+
console.log('Status:', response.status);
|
|
46
|
+
console.log('Output Text:', response.output_text);
|
|
47
|
+
console.log('Usage:', response.usage);
|
|
48
|
+
} else {
|
|
49
|
+
throw new Error('Responses API not available');
|
|
50
|
+
}
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.log('️ Responses API not yet available in this OpenAI SDK version');
|
|
53
|
+
console.log(' Error:', (error as Error).message);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
console.log('\n' + '='.repeat(50) + '\n');
|
|
57
|
+
|
|
58
|
+
// Example 2: Responses API with rich metadata
|
|
59
|
+
console.log(' Example 2: Responses API with rich metadata');
|
|
60
|
+
try {
|
|
61
|
+
const responsesAPI = openai as any;
|
|
62
|
+
|
|
63
|
+
if (responsesAPI.responses?.create) {
|
|
64
|
+
const response: ResponsesResponse = await responsesAPI.responses.create({
|
|
65
|
+
model: 'gpt-5',
|
|
66
|
+
input: [
|
|
67
|
+
{
|
|
68
|
+
role: 'user',
|
|
69
|
+
content: 'Explain quantum computing in simple terms.',
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
max_output_tokens: 150,
|
|
73
|
+
usageMetadata: {
|
|
74
|
+
subscriber: {
|
|
75
|
+
id: 'user-123',
|
|
76
|
+
email: 'user@example.com',
|
|
77
|
+
credential: {
|
|
78
|
+
name: 'api-key',
|
|
79
|
+
value: 'sk-...',
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
organizationId: 'org-456',
|
|
83
|
+
productId: 'quantum-explainer',
|
|
84
|
+
taskType: 'educational-content',
|
|
85
|
+
traceId: 'trace-789',
|
|
86
|
+
agent: 'quantum-tutor',
|
|
87
|
+
responseQualityScore: 0.95,
|
|
88
|
+
},
|
|
89
|
+
} as ResponsesCreateParams);
|
|
90
|
+
|
|
91
|
+
console.log('Response ID:', response.id);
|
|
92
|
+
console.log('Model:', response.model);
|
|
93
|
+
console.log('Status:', response.status);
|
|
94
|
+
console.log('Output Text:', response.output_text?.substring(0, 100) + '...');
|
|
95
|
+
console.log('Usage:', response.usage);
|
|
96
|
+
} else {
|
|
97
|
+
throw new Error('Responses API not available');
|
|
98
|
+
}
|
|
99
|
+
} catch (error) {
|
|
100
|
+
console.log('️ Responses API not yet available in this OpenAI SDK version');
|
|
101
|
+
console.log(' Error:', (error as Error).message);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
console.log('\n' + '='.repeat(50) + '\n');
|
|
105
|
+
|
|
106
|
+
// Example 3: Basic Responses API with string input (no metadata)
|
|
107
|
+
console.log(' Example 3: Basic Responses API with string input (no metadata)');
|
|
108
|
+
try {
|
|
109
|
+
const responsesAPI = openai as any;
|
|
110
|
+
|
|
111
|
+
if (responsesAPI.responses?.create) {
|
|
112
|
+
const response: ResponsesResponse = await responsesAPI.responses.create({
|
|
113
|
+
model: 'gpt-5',
|
|
114
|
+
input: 'Write a haiku about programming.',
|
|
115
|
+
} as ResponsesCreateParams);
|
|
116
|
+
|
|
117
|
+
console.log('Response ID:', response.id);
|
|
118
|
+
console.log('Model:', response.model);
|
|
119
|
+
console.log('Status:', response.status);
|
|
120
|
+
console.log('Output Text:', response.output_text);
|
|
121
|
+
console.log('Usage:', response.usage);
|
|
122
|
+
} else {
|
|
123
|
+
throw new Error('Responses API not available');
|
|
124
|
+
}
|
|
125
|
+
} catch (error) {
|
|
126
|
+
console.log('️ Responses API not yet available in this OpenAI SDK version');
|
|
127
|
+
console.log(' Error:', (error as Error).message);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
console.log('\n' + '='.repeat(50) + '\n');
|
|
131
|
+
|
|
132
|
+
// Example 4: Advanced Responses API with comprehensive metadata
|
|
133
|
+
console.log(' Example 4: Advanced Responses API with comprehensive metadata');
|
|
134
|
+
try {
|
|
135
|
+
const responsesAPI = openai as any;
|
|
136
|
+
|
|
137
|
+
if (responsesAPI.responses?.create) {
|
|
138
|
+
const response: ResponsesResponse = await responsesAPI.responses.create({
|
|
139
|
+
model: 'gpt-5',
|
|
140
|
+
input: [
|
|
141
|
+
{
|
|
142
|
+
role: 'user',
|
|
143
|
+
content: 'Provide a comprehensive overview of the Responses API capabilities.',
|
|
144
|
+
},
|
|
145
|
+
],
|
|
146
|
+
max_output_tokens: 200,
|
|
147
|
+
instructions: 'You are a helpful AI assistant specializing in API documentation.',
|
|
148
|
+
usageMetadata: {
|
|
149
|
+
subscriber: {
|
|
150
|
+
id: 'enterprise-user-456',
|
|
151
|
+
email: 'enterprise@company.com',
|
|
152
|
+
credential: {
|
|
153
|
+
name: 'enterprise-key',
|
|
154
|
+
value: 'sk-enterprise-...',
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
organizationId: 'enterprise-org-789',
|
|
158
|
+
productId: 'api-documentation-assistant',
|
|
159
|
+
taskType: 'technical-documentation',
|
|
160
|
+
traceId: 'enterprise-trace-012',
|
|
161
|
+
agent: 'documentation-expert',
|
|
162
|
+
responseQualityScore: 0.98,
|
|
163
|
+
},
|
|
164
|
+
} as ResponsesCreateParams);
|
|
165
|
+
|
|
166
|
+
console.log('Response ID:', response.id);
|
|
167
|
+
console.log('Model:', response.model);
|
|
168
|
+
console.log('Status:', response.status);
|
|
169
|
+
console.log('Output Text:', response.output_text?.substring(0, 150) + '...');
|
|
170
|
+
console.log('Usage:', response.usage);
|
|
171
|
+
console.log('Output Array Length:', response.output?.length);
|
|
172
|
+
} else {
|
|
173
|
+
throw new Error('Responses API not available');
|
|
174
|
+
}
|
|
175
|
+
} catch (error) {
|
|
176
|
+
console.log('️ Responses API not yet available in this OpenAI SDK version');
|
|
177
|
+
console.log(' Error:', (error as Error).message);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
console.log('\n All Responses API examples completed!');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI Responses API Streaming Examples
|
|
3
|
+
*
|
|
4
|
+
* This file demonstrates how to use the new OpenAI Responses API with streaming enabled
|
|
5
|
+
* using the Revenium middleware. The Responses API supports streaming for real-time
|
|
6
|
+
* response generation.
|
|
7
|
+
*
|
|
8
|
+
* Reference: https://learn.microsoft.com/en-us/azure/ai-foundry/openai/how-to/responses
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import 'dotenv/config';
|
|
12
|
+
import { initializeReveniumFromEnv, patchOpenAIInstance } from '@revenium/openai';
|
|
13
|
+
import OpenAI from 'openai';
|
|
14
|
+
|
|
15
|
+
// Import types for the new Responses API
|
|
16
|
+
import type { ResponsesCreateParams } from '../src/types/responses-api.js';
|
|
17
|
+
|
|
18
|
+
async function main() {
|
|
19
|
+
// Initialize Revenium middleware
|
|
20
|
+
await initializeReveniumFromEnv();
|
|
21
|
+
|
|
22
|
+
// Create OpenAI client
|
|
23
|
+
const openai = new OpenAI({
|
|
24
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Patch the OpenAI instance to add Revenium tracking
|
|
28
|
+
patchOpenAIInstance(openai);
|
|
29
|
+
|
|
30
|
+
console.log(' OpenAI Responses API Streaming Examples\n');
|
|
31
|
+
|
|
32
|
+
// Example 1: Basic Responses API streaming (no metadata)
|
|
33
|
+
console.log(' Example 1: Basic Responses API streaming (no metadata)');
|
|
34
|
+
try {
|
|
35
|
+
const responsesAPI = openai as any; // Type assertion for new API
|
|
36
|
+
|
|
37
|
+
if (responsesAPI.responses?.create) {
|
|
38
|
+
const stream = await responsesAPI.responses.create({
|
|
39
|
+
model: 'gpt-5',
|
|
40
|
+
input: 'Tell me a short story about a robot learning to paint.',
|
|
41
|
+
stream: true,
|
|
42
|
+
} as ResponsesCreateParams);
|
|
43
|
+
|
|
44
|
+
console.log('Streaming response:');
|
|
45
|
+
for await (const event of stream) {
|
|
46
|
+
if (event.type === 'response.output_text.delta') {
|
|
47
|
+
process.stdout.write(event.delta);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
console.log('\n Stream completed');
|
|
51
|
+
} else {
|
|
52
|
+
throw new Error('Responses API not available');
|
|
53
|
+
}
|
|
54
|
+
} catch (error) {
|
|
55
|
+
console.log('️ Responses API not yet available in this OpenAI SDK version');
|
|
56
|
+
console.log(' Error:', (error as Error).message);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
console.log('\n' + '='.repeat(50) + '\n');
|
|
60
|
+
|
|
61
|
+
// Example 2: Responses API streaming with rich metadata
|
|
62
|
+
console.log(' Example 2: Responses API streaming with rich metadata');
|
|
63
|
+
try {
|
|
64
|
+
const responsesAPI = openai as any;
|
|
65
|
+
|
|
66
|
+
if (responsesAPI.responses?.create) {
|
|
67
|
+
const stream = await responsesAPI.responses.create({
|
|
68
|
+
model: 'gpt-5',
|
|
69
|
+
input: [
|
|
70
|
+
{
|
|
71
|
+
role: 'user',
|
|
72
|
+
content: 'Explain the concept of machine learning in a conversational way.',
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
stream: true,
|
|
76
|
+
max_output_tokens: 200,
|
|
77
|
+
usageMetadata: {
|
|
78
|
+
subscriber: {
|
|
79
|
+
id: 'streaming-user-123',
|
|
80
|
+
email: 'streaming@example.com',
|
|
81
|
+
credential: {
|
|
82
|
+
name: 'streaming-key',
|
|
83
|
+
value: 'sk-streaming-...',
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
organizationId: 'streaming-org-456',
|
|
87
|
+
productId: 'ml-educator',
|
|
88
|
+
taskType: 'educational-streaming',
|
|
89
|
+
traceId: 'stream-trace-789',
|
|
90
|
+
agent: 'ml-tutor-stream',
|
|
91
|
+
responseQualityScore: 0.92,
|
|
92
|
+
},
|
|
93
|
+
} as ResponsesCreateParams);
|
|
94
|
+
|
|
95
|
+
console.log('Streaming response with metadata:');
|
|
96
|
+
for await (const event of stream) {
|
|
97
|
+
if (event.type === 'response.output_text.delta') {
|
|
98
|
+
process.stdout.write(event.delta);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
console.log('\n Stream with metadata completed');
|
|
102
|
+
} else {
|
|
103
|
+
throw new Error('Responses API not available');
|
|
104
|
+
}
|
|
105
|
+
} catch (error) {
|
|
106
|
+
console.log('️ Responses API not yet available in this OpenAI SDK version');
|
|
107
|
+
console.log(' Error:', (error as Error).message);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
console.log('\n' + '='.repeat(50) + '\n');
|
|
111
|
+
|
|
112
|
+
// Example 3: Basic Responses API streaming with array input (no metadata)
|
|
113
|
+
console.log(' Example 3: Basic Responses API streaming with array input (no metadata)');
|
|
114
|
+
try {
|
|
115
|
+
const responsesAPI = openai as any;
|
|
116
|
+
|
|
117
|
+
if (responsesAPI.responses?.create) {
|
|
118
|
+
const stream = await responsesAPI.responses.create({
|
|
119
|
+
model: 'gpt-5',
|
|
120
|
+
input: [
|
|
121
|
+
{
|
|
122
|
+
role: 'user',
|
|
123
|
+
content: 'Write a poem about the beauty of code.',
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
stream: true,
|
|
127
|
+
} as ResponsesCreateParams);
|
|
128
|
+
|
|
129
|
+
console.log('Streaming poetry:');
|
|
130
|
+
for await (const event of stream) {
|
|
131
|
+
if (event.type === 'response.output_text.delta') {
|
|
132
|
+
process.stdout.write(event.delta);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
console.log('\n Poetry stream completed');
|
|
136
|
+
} else {
|
|
137
|
+
throw new Error('Responses API not available');
|
|
138
|
+
}
|
|
139
|
+
} catch (error) {
|
|
140
|
+
console.log('️ Responses API not yet available in this OpenAI SDK version');
|
|
141
|
+
console.log(' Error:', (error as Error).message);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
console.log('\n' + '='.repeat(50) + '\n');
|
|
145
|
+
|
|
146
|
+
// Example 4: Advanced Responses API streaming with comprehensive metadata
|
|
147
|
+
console.log(' Example 4: Advanced Responses API streaming with comprehensive metadata');
|
|
148
|
+
try {
|
|
149
|
+
const responsesAPI = openai as any;
|
|
150
|
+
|
|
151
|
+
if (responsesAPI.responses?.create) {
|
|
152
|
+
const stream = await responsesAPI.responses.create({
|
|
153
|
+
model: 'gpt-5',
|
|
154
|
+
input: [
|
|
155
|
+
{
|
|
156
|
+
role: 'user',
|
|
157
|
+
content:
|
|
158
|
+
'Provide a detailed explanation of how streaming APIs work in real-time applications.',
|
|
159
|
+
},
|
|
160
|
+
],
|
|
161
|
+
stream: true,
|
|
162
|
+
max_output_tokens: 300,
|
|
163
|
+
instructions:
|
|
164
|
+
'You are a technical expert explaining streaming APIs with practical examples.',
|
|
165
|
+
usageMetadata: {
|
|
166
|
+
subscriber: {
|
|
167
|
+
id: 'advanced-streaming-user-789',
|
|
168
|
+
email: 'advanced@enterprise.com',
|
|
169
|
+
credential: {
|
|
170
|
+
name: 'enterprise-streaming-key',
|
|
171
|
+
value: 'sk-enterprise-streaming-...',
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
organizationId: 'enterprise-streaming-org-012',
|
|
175
|
+
productId: 'streaming-api-educator',
|
|
176
|
+
taskType: 'advanced-technical-streaming',
|
|
177
|
+
traceId: 'advanced-stream-trace-345',
|
|
178
|
+
agent: 'streaming-expert',
|
|
179
|
+
responseQualityScore: 0.97,
|
|
180
|
+
},
|
|
181
|
+
} as ResponsesCreateParams);
|
|
182
|
+
|
|
183
|
+
console.log('Advanced streaming response:');
|
|
184
|
+
let deltaCount = 0;
|
|
185
|
+
for await (const event of stream) {
|
|
186
|
+
if (event.type === 'response.output_text.delta') {
|
|
187
|
+
process.stdout.write(event.delta);
|
|
188
|
+
deltaCount++;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
console.log(`\n Advanced stream completed (${deltaCount} delta events)`);
|
|
192
|
+
} else {
|
|
193
|
+
throw new Error('Responses API not available');
|
|
194
|
+
}
|
|
195
|
+
} catch (error) {
|
|
196
|
+
console.log('️ Responses API not yet available in this OpenAI SDK version');
|
|
197
|
+
console.log(' Error:', (error as Error).message);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
console.log('\n All Responses API streaming examples completed!');
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI Streaming Example
|
|
3
|
+
*
|
|
4
|
+
* Shows how to use Revenium middleware with streaming OpenAI responses and batch embeddings.
|
|
5
|
+
* Demonstrates seamless metadata integration with streaming - all metadata fields are optional!
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import 'dotenv/config';
|
|
9
|
+
import { initializeReveniumFromEnv, patchOpenAIInstance } from '@revenium/openai';
|
|
10
|
+
import OpenAI from 'openai';
|
|
11
|
+
|
|
12
|
+
async function openaiStreamingExample() {
|
|
13
|
+
console.log(' OpenAI Streaming with Seamless Metadata Integration\n');
|
|
14
|
+
|
|
15
|
+
// Initialize Revenium middleware
|
|
16
|
+
const initResult = initializeReveniumFromEnv();
|
|
17
|
+
if (!initResult.success) {
|
|
18
|
+
console.error(' Failed to initialize Revenium:', initResult.message);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Create and patch OpenAI instance
|
|
23
|
+
const openai = patchOpenAIInstance(new OpenAI());
|
|
24
|
+
|
|
25
|
+
// Example 1: Basic streaming (no metadata)
|
|
26
|
+
console.log(' Example 1: Basic streaming chat (automatic tracking)');
|
|
27
|
+
console.log(' Assistant: ');
|
|
28
|
+
|
|
29
|
+
const basicStream = await openai.chat.completions.create({
|
|
30
|
+
model: 'gpt-4o-mini',
|
|
31
|
+
messages: [{ role: 'user', content: 'Count from 1 to 5 slowly' }],
|
|
32
|
+
stream: true,
|
|
33
|
+
// No usageMetadata - still automatically tracked when stream completes!
|
|
34
|
+
// No max_tokens - let response complete naturally
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
for await (const chunk of basicStream) {
|
|
38
|
+
const content = chunk.choices[0]?.delta?.content || '';
|
|
39
|
+
if (content) {
|
|
40
|
+
process.stdout.write(content);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
console.log('\n Streaming automatically tracked to Revenium without metadata\n');
|
|
45
|
+
|
|
46
|
+
// Example 2: Streaming with rich metadata (all optional!)
|
|
47
|
+
console.log(' Example 2: Streaming chat with rich metadata');
|
|
48
|
+
console.log(' Assistant: ');
|
|
49
|
+
|
|
50
|
+
const metadataStream = await openai.chat.completions.create({
|
|
51
|
+
model: 'gpt-4o-mini',
|
|
52
|
+
messages: [{ role: 'user', content: 'Write a haiku about middleware' }],
|
|
53
|
+
stream: true,
|
|
54
|
+
|
|
55
|
+
// All metadata fields are optional - add what you need for analytics!
|
|
56
|
+
// Note: Nested subscriber structure matches Python middleware implementation
|
|
57
|
+
usageMetadata: {
|
|
58
|
+
// User tracking (optional) - nested subscriber object
|
|
59
|
+
subscriber: {
|
|
60
|
+
id: 'streaming-user-456',
|
|
61
|
+
email: 'poet@company.com',
|
|
62
|
+
credential: {
|
|
63
|
+
name: 'stream-key',
|
|
64
|
+
value: 'stream456',
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
// Business context (optional)
|
|
69
|
+
organizationId: 'creative-company',
|
|
70
|
+
productId: 'ai-poet',
|
|
71
|
+
|
|
72
|
+
// Task classification (optional)
|
|
73
|
+
taskType: 'creative-writing',
|
|
74
|
+
traceId: `stream-${Date.now()}`,
|
|
75
|
+
|
|
76
|
+
// Custom fields (optional)
|
|
77
|
+
agent: 'openai-streaming-chat-metadata-node',
|
|
78
|
+
responseQualityScore: 0.9,
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
for await (const chunk of metadataStream) {
|
|
83
|
+
const content = chunk.choices[0]?.delta?.content || '';
|
|
84
|
+
if (content) {
|
|
85
|
+
process.stdout.write(content);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
console.log('\n Streaming tracked with rich metadata for analytics\n');
|
|
90
|
+
|
|
91
|
+
// Example 3: Batch embeddings (no metadata)
|
|
92
|
+
console.log(' Example 3: Batch embeddings (automatic tracking)');
|
|
93
|
+
|
|
94
|
+
const batchEmbeddings = await openai.embeddings.create({
|
|
95
|
+
model: 'text-embedding-3-small',
|
|
96
|
+
input: [
|
|
97
|
+
'First document for batch processing',
|
|
98
|
+
'Second document for batch processing',
|
|
99
|
+
'Third document for batch processing',
|
|
100
|
+
],
|
|
101
|
+
// No usageMetadata - still automatically tracked!
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
console.log(' Model:', batchEmbeddings.model);
|
|
105
|
+
console.log(' Usage:', batchEmbeddings.usage);
|
|
106
|
+
console.log(' Embeddings count:', batchEmbeddings.data.length);
|
|
107
|
+
console.log(' Batch embeddings automatically tracked without metadata\n');
|
|
108
|
+
|
|
109
|
+
// Example 4: Embeddings with metadata for batch processing
|
|
110
|
+
console.log(' Example 4: Batch embeddings with metadata');
|
|
111
|
+
|
|
112
|
+
const metadataBatchEmbeddings = await openai.embeddings.create({
|
|
113
|
+
model: 'text-embedding-3-small',
|
|
114
|
+
input: [
|
|
115
|
+
'Document 1: Streaming responses provide real-time feedback',
|
|
116
|
+
'Document 2: Metadata enables rich business analytics',
|
|
117
|
+
'Document 3: Batch processing improves efficiency',
|
|
118
|
+
],
|
|
119
|
+
|
|
120
|
+
// All metadata fields are optional - perfect for batch operations!
|
|
121
|
+
usageMetadata: {
|
|
122
|
+
// User tracking (optional) - nested subscriber object
|
|
123
|
+
subscriber: {
|
|
124
|
+
id: 'batch-processor-123',
|
|
125
|
+
email: 'batch@data-company.com',
|
|
126
|
+
credential: {
|
|
127
|
+
name: 'batch-key',
|
|
128
|
+
value: 'batch-value-456',
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
// Business context (optional)
|
|
133
|
+
organizationId: 'data-company',
|
|
134
|
+
productId: 'document-search',
|
|
135
|
+
|
|
136
|
+
// Task classification (optional)
|
|
137
|
+
taskType: 'batch-document-embedding',
|
|
138
|
+
traceId: `batch-${Date.now()}`,
|
|
139
|
+
|
|
140
|
+
// Custom fields (optional)
|
|
141
|
+
agent: 'openai-batch-embeddings-metadata-node',
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
console.log(' Model:', metadataBatchEmbeddings.model);
|
|
146
|
+
console.log(' Usage:', metadataBatchEmbeddings.usage);
|
|
147
|
+
console.log(' Embeddings count:', metadataBatchEmbeddings.data.length);
|
|
148
|
+
console.log(' Batch embeddings tracked with metadata for business insights\n');
|
|
149
|
+
|
|
150
|
+
// Summary
|
|
151
|
+
console.log(' Summary:');
|
|
152
|
+
console.log(' Streaming responses work seamlessly with metadata');
|
|
153
|
+
console.log(' Usage tracked automatically when streams complete');
|
|
154
|
+
console.log(' Batch embeddings supported with optional metadata');
|
|
155
|
+
console.log(' All metadata fields are optional');
|
|
156
|
+
console.log(' No type casting required - native TypeScript support');
|
|
157
|
+
console.log(' Real-time streaming + comprehensive analytics');
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Run the example
|
|
161
|
+
openaiStreamingExample().catch(console.error);
|