cloudbroker-sdk 0.4.0 → 0.5.0
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/package.json +1 -1
- package/src/client.js +2 -2
- package/src/test.js +52 -0
- package/src/wrapper.js +24 -4
package/package.json
CHANGED
package/src/client.js
CHANGED
|
@@ -37,7 +37,7 @@ export class CloudBrokerClient {
|
|
|
37
37
|
try {
|
|
38
38
|
const payload = {
|
|
39
39
|
...event,
|
|
40
|
-
sdkVersion: "0.
|
|
40
|
+
sdkVersion: "0.5.0",
|
|
41
41
|
sentAt: new Date().toISOString(),
|
|
42
42
|
};
|
|
43
43
|
if (this.#cloudQueue.length < MAX_QUEUE_SIZE) {
|
|
@@ -93,7 +93,7 @@ export class CloudBrokerClient {
|
|
|
93
93
|
try {
|
|
94
94
|
const payload = {
|
|
95
95
|
...event,
|
|
96
|
-
sdkVersion: "0.
|
|
96
|
+
sdkVersion: "0.5.0",
|
|
97
97
|
sentAt: new Date().toISOString(),
|
|
98
98
|
};
|
|
99
99
|
|
package/src/test.js
CHANGED
|
@@ -173,5 +173,57 @@ assert.strictEqual(ctxEvent.product, "Billing", `product do contexto esperad
|
|
|
173
173
|
assert.strictEqual(ctxEvent.team, "Plataforma", `team do contexto esperado, veio ${ctxEvent.team}`);
|
|
174
174
|
console.log("✅ expressMiddleware: label/product/team do contexto aplicados sem cb* explícito");
|
|
175
175
|
|
|
176
|
+
// 4) CloudBrokerAI.wrap — label de código nas chamadas de IA (mesma
|
|
177
|
+
// convenção do wrapAWS: explícito na chamada > contexto cbStore > nenhum),
|
|
178
|
+
// pra agregar custo de IA junto com custo de nuvem na visão "Custo por Código".
|
|
179
|
+
const capturedAi = [];
|
|
180
|
+
globalThis.fetch = async (url, opts) => {
|
|
181
|
+
if (String(url).includes("/api/ai/events")) {
|
|
182
|
+
capturedAi.push(JSON.parse(opts.body));
|
|
183
|
+
}
|
|
184
|
+
return { ok: true, status: 200, json: async () => ({}) };
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
let sawOpenAiOptions;
|
|
188
|
+
const mockOpenAI2 = {
|
|
189
|
+
chat: {
|
|
190
|
+
completions: {
|
|
191
|
+
create: async (params, options) => {
|
|
192
|
+
sawOpenAiOptions = options;
|
|
193
|
+
return {
|
|
194
|
+
model: params.model,
|
|
195
|
+
choices: [{ message: { role: "assistant", content: "ok" } }],
|
|
196
|
+
usage: { prompt_tokens: 10, completion_tokens: 5 },
|
|
197
|
+
};
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
};
|
|
202
|
+
const wrappedAi = CloudBrokerAI.wrap(mockOpenAI2, {
|
|
203
|
+
apiKey: "cb-test-key", product: "FinOps Advisor", team: "platform", debug: true,
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
await wrappedAi.chat.completions.create(
|
|
207
|
+
{ model: "gpt-4o", messages: [{ role: "user", content: "x" }] },
|
|
208
|
+
{ cbLabel: "finops-advisor:intent-classify" }
|
|
209
|
+
);
|
|
210
|
+
assert.strictEqual(sawOpenAiOptions, undefined, "cbLabel não pode vazar pro SDK do provider");
|
|
211
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
212
|
+
assert.strictEqual(capturedAi.length, 1, `esperado 1 evento de IA, veio ${capturedAi.length}`);
|
|
213
|
+
assert.strictEqual(capturedAi[0].events[0].label, "finops-advisor:intent-classify");
|
|
214
|
+
console.log("✅ CloudBrokerAI.wrap: label explícito (cbLabel) capturado, sem vazar pro provider");
|
|
215
|
+
|
|
216
|
+
// label via contexto (cbStore, mesmo ALS de CloudBrokerCloud.expressMiddleware)
|
|
217
|
+
const mw2 = CloudBrokerCloud.expressMiddleware({ label: "finops-advisor:root-cause-agent" });
|
|
218
|
+
await new Promise((resolve) => {
|
|
219
|
+
mw2({ method: "POST", path: "/api/chat/response" }, {}, async () => {
|
|
220
|
+
await wrappedAi.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "y" }] });
|
|
221
|
+
resolve();
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
225
|
+
assert.strictEqual(capturedAi[1].events[0].label, "finops-advisor:root-cause-agent");
|
|
226
|
+
console.log("✅ CloudBrokerAI.wrap: label via contexto cbStore (sem cbLabel explícito)");
|
|
227
|
+
|
|
176
228
|
globalThis.fetch = realFetch;
|
|
177
229
|
console.log("\n🎉 Todos os testes passaram.");
|
package/src/wrapper.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { CloudBrokerClient } from "./client.js";
|
|
7
7
|
import { PRICING } from "./pricing.js";
|
|
8
|
+
import { cbStore } from "./context.js";
|
|
8
9
|
|
|
9
10
|
export class CloudBrokerAI {
|
|
10
11
|
/**
|
|
@@ -17,6 +18,13 @@ export class CloudBrokerAI {
|
|
|
17
18
|
* @param {string} options.team — nome do squad (ex: "mobile")
|
|
18
19
|
* @param {string} options.endpoint — endpoint CloudBroker (opcional)
|
|
19
20
|
* @param {boolean} options.debug — logs de debug (opcional)
|
|
21
|
+
*
|
|
22
|
+
* Label opcional do trecho de código, pra agregar custo de IA junto com
|
|
23
|
+
* custo de nuvem na mesma visão "Custo por Código" (mesma convenção de
|
|
24
|
+
* CloudBrokerCloud.wrapAWS):
|
|
25
|
+
* await client.chat.completions.create(params, { cbLabel: "checkout:resumo-pedido" });
|
|
26
|
+
* await client.messages.create(params, { cbLabel: "checkout:resumo-pedido" });
|
|
27
|
+
* Ou via CloudBrokerCloud.expressMiddleware() (contexto automático por requisição).
|
|
20
28
|
*/
|
|
21
29
|
static wrap(client, options = {}) {
|
|
22
30
|
const { apiKey, product, team, endpoint, debug = false } = options;
|
|
@@ -77,6 +85,11 @@ export class CloudBrokerAI {
|
|
|
77
85
|
|
|
78
86
|
client.chat.completions.create = async (params, options) => {
|
|
79
87
|
const start = Date.now();
|
|
88
|
+
// Mesma precedência de cloud-wrapper.js: explícito na chamada > contexto
|
|
89
|
+
// cbStore (expressMiddleware) > nenhum. Nunca vaza cbLabel pro SDK do provider.
|
|
90
|
+
const cbLabel = options?.cbLabel || cbStore.getStore()?.label || null;
|
|
91
|
+
const { cbLabel: _l, ...providerOptions } = options || {};
|
|
92
|
+
const fwdOptions = Object.keys(providerOptions).length ? providerOptions : undefined;
|
|
80
93
|
|
|
81
94
|
// ── Streaming: pede usage no chunk final e instrumenta o iterador ────────
|
|
82
95
|
if (params?.stream) {
|
|
@@ -84,7 +97,7 @@ export class CloudBrokerAI {
|
|
|
84
97
|
...params,
|
|
85
98
|
stream_options: { include_usage: true, ...(params.stream_options || {}) },
|
|
86
99
|
};
|
|
87
|
-
const stream = await originalCreate(streamParams,
|
|
100
|
+
const stream = await originalCreate(streamParams, fwdOptions);
|
|
88
101
|
|
|
89
102
|
let model = params.model || "unknown";
|
|
90
103
|
let inputTokens = 0, outputTokens = 0;
|
|
@@ -104,6 +117,7 @@ export class CloudBrokerAI {
|
|
|
104
117
|
model,
|
|
105
118
|
product: product || null,
|
|
106
119
|
team: team || null,
|
|
120
|
+
label: cbLabel,
|
|
107
121
|
inputTokens,
|
|
108
122
|
outputTokens,
|
|
109
123
|
totalTokens: inputTokens + outputTokens,
|
|
@@ -121,7 +135,7 @@ export class CloudBrokerAI {
|
|
|
121
135
|
}
|
|
122
136
|
|
|
123
137
|
// ── Não-streaming ───────────────────────────────────────────────────────
|
|
124
|
-
const res = await originalCreate(params,
|
|
138
|
+
const res = await originalCreate(params, fwdOptions);
|
|
125
139
|
const ms = Date.now() - start;
|
|
126
140
|
|
|
127
141
|
// Fire-and-forget
|
|
@@ -137,6 +151,7 @@ export class CloudBrokerAI {
|
|
|
137
151
|
model,
|
|
138
152
|
product: product || null,
|
|
139
153
|
team: team || null,
|
|
154
|
+
label: cbLabel,
|
|
140
155
|
inputTokens,
|
|
141
156
|
outputTokens,
|
|
142
157
|
totalTokens: inputTokens + outputTokens,
|
|
@@ -159,10 +174,13 @@ export class CloudBrokerAI {
|
|
|
159
174
|
|
|
160
175
|
client.messages.create = async (params, options) => {
|
|
161
176
|
const start = Date.now();
|
|
177
|
+
const cbLabel = options?.cbLabel || cbStore.getStore()?.label || null;
|
|
178
|
+
const { cbLabel: _l, ...providerOptions } = options || {};
|
|
179
|
+
const fwdOptions = Object.keys(providerOptions).length ? providerOptions : undefined;
|
|
162
180
|
|
|
163
181
|
// ── Streaming: acumula usage dos eventos message_start / message_delta ───
|
|
164
182
|
if (params?.stream) {
|
|
165
|
-
const stream = await originalCreate(params,
|
|
183
|
+
const stream = await originalCreate(params, fwdOptions);
|
|
166
184
|
|
|
167
185
|
let model = params.model || "unknown";
|
|
168
186
|
let inputTokens = 0, outputTokens = 0;
|
|
@@ -185,6 +203,7 @@ export class CloudBrokerAI {
|
|
|
185
203
|
model,
|
|
186
204
|
product: product || null,
|
|
187
205
|
team: team || null,
|
|
206
|
+
label: cbLabel,
|
|
188
207
|
inputTokens,
|
|
189
208
|
outputTokens,
|
|
190
209
|
totalTokens: inputTokens + outputTokens,
|
|
@@ -202,7 +221,7 @@ export class CloudBrokerAI {
|
|
|
202
221
|
}
|
|
203
222
|
|
|
204
223
|
// ── Não-streaming ───────────────────────────────────────────────────────
|
|
205
|
-
const res = await originalCreate(params,
|
|
224
|
+
const res = await originalCreate(params, fwdOptions);
|
|
206
225
|
const ms = Date.now() - start;
|
|
207
226
|
|
|
208
227
|
try {
|
|
@@ -217,6 +236,7 @@ export class CloudBrokerAI {
|
|
|
217
236
|
model,
|
|
218
237
|
product: product || null,
|
|
219
238
|
team: team || null,
|
|
239
|
+
label: cbLabel,
|
|
220
240
|
inputTokens,
|
|
221
241
|
outputTokens,
|
|
222
242
|
totalTokens: inputTokens + outputTokens,
|