@rheonic/sdk 0.1.0-beta.10 → 0.1.0-beta.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 CHANGED
@@ -4,9 +4,13 @@ All notable changes to `@rheonic/sdk` will be documented in this file.
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ### Changed
8
+ - Google provider instrumentation and examples now align with the current official Google Gen AI JavaScript SDK shape: `@google/genai` with `ai.models.generateContent({...})`.
9
+
7
10
  ### Fixed
8
11
  - SDK debug logs now always emit non-empty `trace_id` and `span_id`, including warmup, token-estimation, and protect preflight paths.
9
12
  - Provider instrumentation now keeps the full protected call lifecycle under one trace so SDK debug logs correlate cleanly with backend requests.
13
+ - Quickstart and README examples now use the published `@rheonic/sdk` package name consistently and show the current Anthropic and Google instrumentation patterns.
10
14
 
11
15
  ## 0.1.0-beta.7
12
16
 
package/README.md CHANGED
@@ -69,14 +69,18 @@ Anthropic:
69
69
 
70
70
  ```ts
71
71
  import Anthropic from "@anthropic-ai/sdk";
72
- import { createClient, RHEONICBlockedError } from "@rheonic/sdk";
72
+ import { createClient, instrumentAnthropic, RHEONICBlockedError } from "@rheonic/sdk";
73
73
 
74
74
  const rheonic = createClient({
75
75
  baseUrl: process.env.RHEONIC_BASE_URL!,
76
76
  ingestKey: process.env.RHEONIC_INGEST_KEY!,
77
77
  });
78
78
 
79
- const anthropic = rheonic.instrumentAnthropic(new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY! }));
79
+ const anthropic = instrumentAnthropic(new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY! }), {
80
+ client: rheonic,
81
+ endpoint: "/v1/messages",
82
+ feature: "assistant",
83
+ });
80
84
 
81
85
  try {
82
86
  await anthropic.messages.create({
@@ -100,19 +104,28 @@ try {
100
104
  Google:
101
105
 
102
106
  ```ts
103
- import { GoogleGenerativeAI } from "@google/generative-ai";
104
- import { createClient, RHEONICBlockedError } from "@rheonic/sdk";
107
+ import { GoogleGenAI } from "@google/genai";
108
+ import { createClient, instrumentGoogle, RHEONICBlockedError } from "@rheonic/sdk";
105
109
 
106
110
  const rheonic = createClient({
107
111
  baseUrl: process.env.RHEONIC_BASE_URL!,
108
112
  ingestKey: process.env.RHEONIC_INGEST_KEY!,
109
113
  });
110
114
 
111
- const genAI = new GoogleGenerativeAI(process.env.GOOGLE_API_KEY!);
112
- const model = rheonic.instrumentGoogle(genAI.getGenerativeModel({ model: "gemini-1.5-pro" }));
115
+ const ai = instrumentGoogle(new GoogleGenAI({ apiKey: process.env.GOOGLE_API_KEY! }), {
116
+ client: rheonic,
117
+ endpoint: "/v1beta/models/generateContent",
118
+ feature: "assistant",
119
+ });
113
120
 
114
121
  try {
115
- await model.generateContent("hello");
122
+ await ai.models.generateContent({
123
+ model: "gemini-1.5-pro",
124
+ contents: "hello",
125
+ config: {
126
+ maxOutputTokens: 256,
127
+ },
128
+ });
116
129
  } catch (error) {
117
130
  if (error instanceof RHEONICBlockedError) {
118
131
  console.log(JSON.stringify({
@@ -1,6 +1,7 @@
1
1
  export interface EventRequest {
2
2
  endpoint?: string;
3
3
  feature?: string;
4
+ request_fingerprint?: string;
4
5
  input_tokens?: number;
5
6
  input_tokens_estimate?: number;
6
7
  token_explosion_tokens?: number;
@@ -8,17 +8,17 @@ export function __setInputTokenEstimatorForTests(estimator) {
8
8
  estimatorOverrideForTests = estimator;
9
9
  }
10
10
  export function instrumentGoogle(googleModel, options) {
11
- const targetGenerate = googleModel?.generateContent;
12
- if (typeof targetGenerate !== "function") {
11
+ const targetGenerate = resolveGenerateTarget(googleModel);
12
+ if (!targetGenerate) {
13
13
  return googleModel;
14
14
  }
15
- const originalGenerate = targetGenerate.bind(googleModel);
16
- googleModel.generateContent = async (...args) => {
15
+ const originalGenerate = targetGenerate.fn.bind(targetGenerate.owner);
16
+ targetGenerate.owner[targetGenerate.key] = async (...args) => {
17
17
  const traceId = generateTraceId();
18
18
  const spanId = generateSpanId();
19
19
  return bindTraceContext(traceId, spanId, async () => {
20
20
  const startedAt = Date.now();
21
- const requestedModel = extractRequestedModel(googleModel);
21
+ const requestedModel = extractRequestedModel(googleModel, args);
22
22
  validateProviderModel("google", requestedModel);
23
23
  const requestPayload = extractRequestPayload(args, requestedModel);
24
24
  let estimatedInputTokens = null;
@@ -99,6 +99,26 @@ export function instrumentGoogle(googleModel, options) {
99
99
  };
100
100
  return googleModel;
101
101
  }
102
+ function resolveGenerateTarget(googleModel) {
103
+ const directGenerate = googleModel?.generateContent;
104
+ if (typeof directGenerate === "function") {
105
+ return {
106
+ owner: googleModel,
107
+ fn: directGenerate,
108
+ key: "generateContent",
109
+ };
110
+ }
111
+ const models = googleModel?.models;
112
+ const nestedGenerate = models?.generateContent;
113
+ if (models && typeof nestedGenerate === "function") {
114
+ return {
115
+ owner: models,
116
+ fn: nestedGenerate,
117
+ key: "generateContent",
118
+ };
119
+ }
120
+ return null;
121
+ }
102
122
  function extractRequestPayload(args, model) {
103
123
  const firstArg = args[0];
104
124
  if (typeof firstArg === "string") {
@@ -109,8 +129,13 @@ function extractRequestPayload(args, model) {
109
129
  }
110
130
  return null;
111
131
  }
112
- function extractRequestedModel(googleModel) {
132
+ function extractRequestedModel(googleModel, args) {
113
133
  if (!googleModel || typeof googleModel !== "object") {
134
+ const firstArg = args[0];
135
+ if (firstArg && typeof firstArg === "object" && "model" in firstArg) {
136
+ const payloadModel = firstArg.model;
137
+ return typeof payloadModel === "string" ? payloadModel : null;
138
+ }
114
139
  return null;
115
140
  }
116
141
  const withModel = googleModel;
@@ -120,16 +145,35 @@ function extractRequestedModel(googleModel) {
120
145
  if (typeof withModel.modelName === "string") {
121
146
  return withModel.modelName;
122
147
  }
148
+ const firstArg = args[0];
149
+ if (firstArg && typeof firstArg === "object" && "model" in firstArg) {
150
+ const payloadModel = firstArg.model;
151
+ return typeof payloadModel === "string" ? payloadModel : null;
152
+ }
123
153
  return null;
124
154
  }
125
155
  function extractMaxOutputTokens(args) {
126
156
  const firstArg = args[0];
127
157
  if (!firstArg || typeof firstArg !== "object") {
128
- return undefined;
158
+ const secondArg = args[1];
159
+ if (!secondArg || typeof secondArg !== "object") {
160
+ return undefined;
161
+ }
162
+ const options = secondArg;
163
+ const optionsGenerationMax = options.generationConfig?.maxOutputTokens;
164
+ if (typeof optionsGenerationMax === "number") {
165
+ return optionsGenerationMax;
166
+ }
167
+ const optionsConfigMax = options.config?.maxOutputTokens;
168
+ return typeof optionsConfigMax === "number" ? optionsConfigMax : undefined;
129
169
  }
130
170
  const payload = firstArg;
131
- const maxOutput = payload.generationConfig?.maxOutputTokens;
132
- return typeof maxOutput === "number" ? maxOutput : undefined;
171
+ const generationMax = payload.generationConfig?.maxOutputTokens;
172
+ if (typeof generationMax === "number") {
173
+ return generationMax;
174
+ }
175
+ const configMax = payload.config?.maxOutputTokens;
176
+ return typeof configMax === "number" ? configMax : undefined;
133
177
  }
134
178
  function extractTotalTokens(response) {
135
179
  if (!response || typeof response !== "object") {
@@ -189,11 +233,13 @@ function maybeApplyGoogleClamp(args, decision) {
189
233
  const firstArg = nextArgs[0];
190
234
  if (firstArg && typeof firstArg === "object") {
191
235
  const payload = { ...firstArg };
192
- const existingConfig = payload.generationConfig && typeof payload.generationConfig === "object"
193
- ? payload.generationConfig
236
+ const useModernConfig = "config" in payload || "contents" in payload || "model" in payload;
237
+ const configKey = useModernConfig ? "config" : "generationConfig";
238
+ const existingConfig = payload[configKey] && typeof payload[configKey] === "object"
239
+ ? payload[configKey]
194
240
  : {};
195
241
  const existingMax = existingConfig.maxOutputTokens;
196
- payload.generationConfig = {
242
+ payload[configKey] = {
197
243
  ...existingConfig,
198
244
  maxOutputTokens: typeof existingMax === "number" ? Math.min(existingMax, recommended) : recommended,
199
245
  };
@@ -201,14 +247,15 @@ function maybeApplyGoogleClamp(args, decision) {
201
247
  return nextArgs;
202
248
  }
203
249
  const secondArg = nextArgs[1];
204
- const existingConfig = secondArg && typeof secondArg === "object" ? { ...secondArg } : {};
205
- const generationConfig = existingConfig.generationConfig && typeof existingConfig.generationConfig === "object"
206
- ? { ...existingConfig.generationConfig }
250
+ const existingOptions = secondArg && typeof secondArg === "object" ? { ...secondArg } : {};
251
+ const configKey = existingOptions.config && typeof existingOptions.config === "object" ? "config" : "generationConfig";
252
+ const generationConfig = existingOptions[configKey] && typeof existingOptions[configKey] === "object"
253
+ ? { ...existingOptions[configKey] }
207
254
  : {};
208
255
  const existingMax = generationConfig.maxOutputTokens;
209
256
  generationConfig.maxOutputTokens = typeof existingMax === "number" ? Math.min(existingMax, recommended) : recommended;
210
- existingConfig.generationConfig = generationConfig;
211
- nextArgs[1] = existingConfig;
257
+ existingOptions[configKey] = generationConfig;
258
+ nextArgs[1] = existingOptions;
212
259
  return nextArgs;
213
260
  }
214
261
  function markClampAppliedIfChanged(decision, originalMaxTokens, appliedMaxTokens) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rheonic/sdk",
3
- "version": "0.1.0-beta.10",
3
+ "version": "0.1.0-beta.11",
4
4
  "description": "Node.js SDK for Rheonic observability and protect preflight enforcement.",
5
5
  "author": "Rheonic <founder@rheonic.dev>",
6
6
  "license": "MIT",