opik-gemini 0.1.1

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/README.md ADDED
@@ -0,0 +1,293 @@
1
+ # Opik Gemini Integration
2
+
3
+ [![npm version](https://img.shields.io/npm/v/opik-gemini.svg)](https://www.npmjs.com/package/opik-gemini)
4
+ [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/comet-ml/opik/blob/main/LICENSE)
5
+
6
+ Seamlessly integrate [Opik](https://www.comet.com/docs/opik/) observability with your [Google Gemini](https://ai.google.dev/) applications.
7
+
8
+ ## Features
9
+
10
+ - 🔍 **Comprehensive Tracing**: Automatically trace Gemini API calls
11
+ - 📊 **Hierarchical Visualization**: View execution as structured traces and spans
12
+ - 📝 **Detailed Metadata**: Record model names, prompts, completions, token usage
13
+ - 🚨 **Error Handling**: Capture and visualize errors with full context
14
+ - 🏷️ **Custom Tagging**: Add custom tags and metadata to organize traces
15
+ - 🔄 **Streaming Support**: Full support for streamed responses
16
+ - ⚡ **Non-blocking**: Minimal performance impact with async batching
17
+ - 🎯 **Type-Safe**: Full TypeScript support with comprehensive types
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ npm install opik-gemini @google/genai
23
+ ```
24
+
25
+ ### Requirements
26
+
27
+ - Node.js ≥ 18
28
+ - @google/genai SDK (≥ 1.0.0)
29
+ - Opik SDK (automatically installed as peer dependency)
30
+
31
+ **Note**: The official Google GenAI SDK package is `@google/genai` (not `@google/generative-ai`). This is Google Deepmind's unified SDK for both Gemini Developer API and Vertex AI.
32
+
33
+ ## Quick Start
34
+
35
+ ### Basic Usage
36
+
37
+ ```typescript
38
+ import { GoogleGenAI } from "@google/genai";
39
+ import { trackGemini } from "opik-gemini";
40
+
41
+ // Initialize Gemini client
42
+ const genAI = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });
43
+
44
+ // Wrap with Opik tracking
45
+ const trackedGenAI = trackGemini(genAI, {
46
+ traceMetadata: {
47
+ tags: ["production", "my-app"],
48
+ },
49
+ });
50
+
51
+ // Use normally - all calls are automatically tracked
52
+ async function main() {
53
+ const response = await trackedGenAI.models.generateContent({
54
+ model: "gemini-2.0-flash-001",
55
+ contents: "What is the capital of France?",
56
+ });
57
+
58
+ console.log(response.text);
59
+
60
+ // Ensure all traces are sent before exit
61
+ await trackedGenAI.flush();
62
+ }
63
+
64
+ main();
65
+ ```
66
+
67
+ ### Streaming Support
68
+
69
+ ```typescript
70
+ import { GoogleGenAI } from "@google/genai";
71
+ import { trackGemini } from "opik-gemini";
72
+
73
+ const genAI = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });
74
+ const trackedGenAI = trackGemini(genAI);
75
+
76
+ async function streamExample() {
77
+ const response = await trackedGenAI.models.generateContentStream({
78
+ model: "gemini-2.0-flash-001",
79
+ contents: "Write a haiku about AI",
80
+ });
81
+
82
+ // Stream is automatically tracked
83
+ let streamedContent = "";
84
+ for await (const chunk of response) {
85
+ const chunkText = chunk.text;
86
+ if (chunkText) {
87
+ process.stdout.write(chunkText);
88
+ streamedContent += chunkText;
89
+ }
90
+ }
91
+
92
+ console.log("\n");
93
+ await trackedGenAI.flush();
94
+ }
95
+
96
+ streamExample();
97
+ ```
98
+
99
+ ### Using with Existing Opik Client
100
+
101
+ ```typescript
102
+ import { Opik } from "opik";
103
+ import { GoogleGenAI } from "@google/genai";
104
+ import { trackGemini } from "opik-gemini";
105
+
106
+ const opikClient = new Opik({
107
+ projectName: "gemini-project",
108
+ });
109
+
110
+ const genAI = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });
111
+ const trackedGenAI = trackGemini(genAI, {
112
+ client: opikClient,
113
+ traceMetadata: {
114
+ tags: ["gemini", "production"],
115
+ environment: "prod",
116
+ },
117
+ });
118
+
119
+ // All calls will be logged to "gemini-project"
120
+ const response = await trackedGenAI.models.generateContent({
121
+ model: "gemini-2.0-flash-001",
122
+ contents: "Hello, Gemini!",
123
+ });
124
+
125
+ console.log(response.text);
126
+ ```
127
+
128
+ ### Custom Generation Names
129
+
130
+ ```typescript
131
+ import { trackGemini } from "opik-gemini";
132
+
133
+ const trackedGenAI = trackGemini(genAI, {
134
+ generationName: "MyCustomGeminiCall",
135
+ });
136
+
137
+ // Traces will appear as "MyCustomGeminiCall" in Opik
138
+ ```
139
+
140
+ ### Nested Tracing
141
+
142
+ ```typescript
143
+ import { Opik } from "opik";
144
+ import { trackGemini } from "opik-gemini";
145
+
146
+ const opikClient = new Opik();
147
+ const trackedGenAI = trackGemini(genAI, { client: opikClient });
148
+
149
+ async function processQuery(query: string) {
150
+ // Create parent trace
151
+ const trace = opikClient.trace({
152
+ name: "ProcessUserQuery",
153
+ input: { query },
154
+ });
155
+
156
+ // Gemini call will be nested under this trace
157
+ const trackedGenAIWithParent = trackGemini(genAI, {
158
+ parent: trace,
159
+ client: opikClient,
160
+ });
161
+
162
+ const response = await trackedGenAIWithParent.models.generateContent({
163
+ model: "gemini-2.0-flash-001",
164
+ contents: query,
165
+ });
166
+
167
+ trace.update({
168
+ output: { response: response.text },
169
+ });
170
+ trace.end();
171
+
172
+ return response;
173
+ }
174
+ ```
175
+
176
+ ## Configuration
177
+
178
+ ### TrackOpikConfig Options
179
+
180
+ ```typescript
181
+ interface TrackOpikConfig {
182
+ /** Opik client instance (optional, creates singleton if not provided) */
183
+ client?: Opik;
184
+
185
+ /** Custom name for the generation (optional, defaults to method name) */
186
+ generationName?: string;
187
+
188
+ /** Parent trace or span for nested tracing (optional) */
189
+ parent?: Trace | Span;
190
+
191
+ /** Additional metadata for traces (optional) */
192
+ traceMetadata?: {
193
+ tags?: string[];
194
+ [key: string]: unknown;
195
+ };
196
+ }
197
+ ```
198
+
199
+ ## What Gets Tracked
200
+
201
+ The integration automatically captures:
202
+
203
+ - **Input**: Prompt contents and generation config
204
+ - **Output**: Generated text, candidates, and safety ratings
205
+ - **Model**: Model name/version (e.g., "gemini-pro", "gemini-1.5-flash")
206
+ - **Usage**: Token counts (prompt, completion, total)
207
+ - **Metadata**: Provider info, model settings, safety settings
208
+ - **Errors**: Error messages and stack traces
209
+ - **Timing**: Start/end times and duration
210
+
211
+ ## Best Practices
212
+
213
+ 1. **Always call `flush()` before process exit** (especially in short-lived scripts):
214
+
215
+ ```typescript
216
+ await trackedGenAI.flush();
217
+ ```
218
+
219
+ 2. **Use descriptive tags** for easier filtering:
220
+
221
+ ```typescript
222
+ trackGemini(genAI, {
223
+ traceMetadata: {
224
+ tags: ["production", "customer-support", "v2"],
225
+ },
226
+ });
227
+ ```
228
+
229
+ 3. **Reuse Opik client** across your application for consistency:
230
+
231
+ ```typescript
232
+ const opikClient = new Opik({ projectName: "my-project" });
233
+ const trackedGenAI = trackGemini(genAI, { client: opikClient });
234
+ ```
235
+
236
+ 4. **Use nested tracing** for complex workflows to understand call hierarchies.
237
+
238
+ ## Supported Gemini Models
239
+
240
+ This integration supports all Google Gemini models including:
241
+
242
+ - `gemini-2.0-flash-001` (Latest, recommended for most use cases)
243
+ - `gemini-1.5-pro`
244
+ - `gemini-1.5-flash`
245
+ - `gemini-pro`
246
+ - `gemini-pro-vision`
247
+ - Any future Gemini models
248
+
249
+ Refer to [Google's official documentation](https://ai.google.dev/models/gemini) for the complete list of available models and their capabilities.
250
+
251
+ ## Development
252
+
253
+ ### Building
254
+
255
+ ```bash
256
+ npm run build
257
+ ```
258
+
259
+ ### Type Checking
260
+
261
+ ```bash
262
+ npm run typecheck
263
+ ```
264
+
265
+ ### Testing
266
+
267
+ ```bash
268
+ npm test
269
+ ```
270
+
271
+ ## Examples
272
+
273
+ Check out the [examples directory](../../../../../../examples) for more usage examples.
274
+
275
+ ## Documentation
276
+
277
+ - [Opik Documentation](https://www.comet.com/docs/opik/)
278
+ - [Google Gemini Documentation](https://ai.google.dev/)
279
+ - [TypeScript SDK Guide](https://www.comet.com/docs/opik/typescript-sdk)
280
+
281
+ ## Support
282
+
283
+ - [GitHub Issues](https://github.com/comet-ml/opik/issues)
284
+ - [Comet Support](mailto:support@comet.com)
285
+ - [Community Slack](https://www.comet.com/docs/opik/community/)
286
+
287
+ ## License
288
+
289
+ Apache-2.0
290
+
291
+ ## Contributing
292
+
293
+ Contributions are welcome! Please see our [Contributing Guide](https://github.com/comet-ml/opik/blob/main/CONTRIBUTING.md).
package/dist/index.cjs ADDED
@@ -0,0 +1,2 @@
1
+ 'use strict';var opik=require('opik');var R=class R{static getInstance(e){return R.instance||(R.instance=new opik.Opik(e)),R.instance}};R.instance=null;var A=R;var x=a=>{if(a)return a.replace(/^models\//,"")};var G=a=>a!==null&&typeof a=="object"&&Symbol.asyncIterator in a&&typeof a[Symbol.asyncIterator]=="function",P=(a,e="")=>{let o={};for(let[n,t]of Object.entries(a)){let i=e?`${e}.${n}`:n;t&&typeof t=="object"&&!Array.isArray(t)&&!(t instanceof Date)?Object.assign(o,P(t,i)):o[i]=t;}return o};var E=a=>{let e=a.model;e&&(e=x(e));let o={};"contents"in a&&(o.contents=a.contents),"prompt"in a&&(o.prompt=a.prompt),"config"in a&&(o.config=a.config);let n={};if("config"in a&&typeof a.config=="object"&&a.config!==null){let t=a.config;"systemInstruction"in t&&(n.systemInstruction=t.systemInstruction),"generationConfig"in t&&(n.generationConfig=t.generationConfig),"safetySettings"in t&&(n.safetySettings=t.safetySettings),"tools"in t&&(n.tools=t.tools),"toolConfig"in t&&(n.toolConfig=t.toolConfig);let i=["temperature","maxOutputTokens","topP","topK","candidateCount","stopSequences"];for(let c of i)c in t&&(n[c]=t[c]);}return {model:e,input:o,modelParameters:n}},O=a=>{if(!a||typeof a!="object")return;let e=a;if("candidates"in e&&Array.isArray(e.candidates))return e.candidates.length===0?void 0:{candidates:e.candidates}},D=a=>{if(!a||typeof a!="object")return;let e=a,o="usageMetadata"in e&&e.usageMetadata||"usage_metadata"in e&&e.usage_metadata||"usage"in e&&e.usage;if(!o||typeof o!="object")return;let n=o,t={};(typeof n.promptTokenCount=="number"||typeof n.prompt_token_count=="number")&&(t.prompt_tokens=n.promptTokenCount||n.prompt_token_count),(typeof n.candidatesTokenCount=="number"||typeof n.candidates_token_count=="number")&&(t.completion_tokens=n.candidatesTokenCount||n.candidates_token_count),(typeof n.totalTokenCount=="number"||typeof n.total_token_count=="number")&&(t.total_tokens=n.totalTokenCount||n.total_token_count),(typeof n.cachedContentTokenCount=="number"||typeof n.cached_content_token_count=="number")&&(t.cached_content_tokens=n.cachedContentTokenCount||n.cached_content_token_count);let i={...n};delete i.promptTokensDetails,delete i.prompt_tokens_details,delete i.candidatesTokensDetails,delete i.candidates_tokens_details;let c=P(i,"original_usage");for(let[l,s]of Object.entries(c))typeof s=="number"&&(t[l]=s);return Object.keys(t).length>0?t:void 0},M=a=>{if(!a||typeof a!="object")return {isToolCall:false,data:""};let e=a;if("candidates"in e&&Array.isArray(e.candidates)&&e.candidates.length>0){let o=e.candidates[0];if(o&&"content"in o&&o.content&&typeof o.content=="object"){let n=o.content;if("parts"in n&&Array.isArray(n.parts)&&n.parts.length>0){let t=n.parts[0];if("functionCall"in t||"function_call"in t)return {isToolCall:true,data:JSON.stringify(t.functionCall||t.function_call)};if("text"in t&&typeof t.text=="string")return {isToolCall:false,data:t.text}}}}return {isToolCall:false,data:""}},C=a=>{if(!a||typeof a!="object")return {model:void 0,metadata:void 0};let e=a,o,n={};if("modelVersion"in e&&typeof e.modelVersion=="string"?o=x(e.modelVersion):"model_version"in e&&typeof e.model_version=="string"&&(o=x(e.model_version)),"candidates"in e&&Array.isArray(e.candidates)&&e.candidates.length>0){let t=e.candidates[0];"safetyRatings"in t?n.safety_ratings=t.safetyRatings:"safety_ratings"in t&&(n.safety_ratings=t.safety_ratings),"finishReason"in t?n.finish_reason=t.finishReason:"finish_reason"in t&&(n.finish_reason=t.finish_reason);}return "promptFeedback"in e?n.prompt_feedback=e.promptFeedback:"prompt_feedback"in e&&(n.prompt_feedback=e.prompt_feedback),n.created_from="genai",{model:o,metadata:Object.keys(n).length>0?n:void 0}};var v=(a,e)=>(...o)=>{var g;let{model:n,input:t,modelParameters:i}=E(o[0]),{tags:c=[],...l}=(g=e==null?void 0:e.traceMetadata)!=null?g:{},s={name:e.generationName,startTime:new Date,input:t,model:n,provider:e.provider,metadata:{...l,...i},tags:["genai",...c]},r,y=!!(e!=null&&e.parent);e!=null&&e.parent?r=e.parent:r=e.client.trace(s);try{let d=a(...o);if(G(d))return N(d,r,y,s);if(d instanceof Promise)return d.then(p=>{if(G(p))return N(p,r,y,s);let F=O(p),S=D(p),{model:$,metadata:K}=C(p),L=r.span({...s,output:F,endTime:new Date,usage:S,model:$||s.model,type:opik.OpikSpanType.Llm,metadata:{...s.metadata,...K,usage:S}}),j=p;return j.functionCalls&&Array.isArray(j.functionCalls)&&j.functionCalls.forEach(h=>{L.span({name:`function_call: ${h.name}`,startTime:new Date,endTime:new Date,input:{arguments:h.args},output:{function_name:h.name},type:opik.OpikSpanType.Tool,metadata:{function_name:h.name,function_arguments:h.args}});}),y||r.update({output:F,endTime:new Date}),p}).catch(p=>{throw I(p,r,s),p});let k=O(d),m=D(d),{model:b,metadata:f}=C(d),u=r.span({...s,output:k,endTime:new Date,usage:m,model:b||s.model,type:opik.OpikSpanType.Llm,metadata:{...s.metadata,...f,usage:m}}),_=d;return _.functionCalls&&Array.isArray(_.functionCalls)&&_.functionCalls.forEach(w=>{u.span({name:`function_call: ${w.name}`,startTime:new Date,endTime:new Date,input:{arguments:w.args},output:{function_name:w.name},type:opik.OpikSpanType.Tool,metadata:{function_name:w.name,function_arguments:w.args}});}),y||r.update({output:k,endTime:new Date}),d}catch(d){throw I(d,r,s),d}};function N(a,e,o,n){async function*t(){var y,g,d,k;let i=[],c=[],l,s={},r;try{for await(let f of a){i.push(f);let u=M(f);u.isToolCall||c.push(u.data),yield f;}let m=i[i.length-1];if(m){l=D(m),r=C(m);let f=c.join(""),u=O(m);if(u!=null&&u.candidates){s={candidates:structuredClone(u.candidates)};let _=s;(k=(d=(g=(y=_.candidates)==null?void 0:y[0])==null?void 0:g.content)==null?void 0:d.parts)!=null&&k[0]&&(_.candidates[0].content.parts[0].text=f);}}let b=e.span({...n,output:s,endTime:new Date,usage:l,model:(r==null?void 0:r.model)||n.model,type:opik.OpikSpanType.Llm,metadata:{...n.metadata,...r==null?void 0:r.metadata,usage:l}});if(m&&typeof m=="object"){let f=m;f.functionCalls&&Array.isArray(f.functionCalls)&&f.functionCalls.forEach(u=>{b.span({name:`function_call: ${u.name}`,startTime:new Date,endTime:new Date,input:{arguments:u.args},output:{function_name:u.name},type:opik.OpikSpanType.Tool,metadata:{function_name:u.name,function_arguments:u.args}});});}o||e.update({output:s,endTime:new Date});}catch(m){throw I(m,e,n),m}}return t()}var I=(a,e,o)=>{var n;e.span({...o,endTime:new Date,type:opik.OpikSpanType.Llm,errorInfo:{message:a.message,exceptionType:a.name,traceback:(n=a.stack)!=null?n:""}}),e.end();};var V=a=>"vertexai"in a&&a.vertexai?"google_vertexai":"google_ai",z=(a,e)=>{let o=V(a);return new Proxy(a,{get(n,t,i){var g,d,k;let c=n[t],l=((g=a.constructor)==null?void 0:g.name)||"Gemini",r={generationName:(d=e==null?void 0:e.generationName)!=null?d:`${l}.${t.toString()}`,client:(k=e==null?void 0:e.client)!=null?k:A.getInstance(),parent:e==null?void 0:e.parent,traceMetadata:e==null?void 0:e.traceMetadata,provider:o};return t==="flush"?r.client.flush.bind(r.client):typeof c=="function"?v(c.bind(n),r):c&&!Array.isArray(c)&&!(c instanceof Date)&&typeof c=="object"?new Proxy(c,{get(m,b,f){let u=m[b];return typeof u=="function"?v(u.bind(m),r):Reflect.get(m,b,f)}}):Reflect.get(n,t,i)}})};
2
+ exports.trackGemini=z;
@@ -0,0 +1,48 @@
1
+ import { Trace, Span, Opik } from 'opik';
2
+
3
+ type OpikParent = Trace | Span;
4
+ type TrackOpikConfig = {
5
+ /**
6
+ * Project name for this trace
7
+ */
8
+ projectName?: string;
9
+ /**
10
+ * Parent span or trace for this generation
11
+ */
12
+ parent?: OpikParent;
13
+ /**
14
+ * Generation name (defaults to "ClassName.methodName")
15
+ */
16
+ generationName?: string;
17
+ /**
18
+ * Trace metadata (tags, custom fields)
19
+ */
20
+ traceMetadata?: Record<string, unknown> & {
21
+ tags?: string[];
22
+ };
23
+ /**
24
+ * Opik client instance
25
+ */
26
+ client?: Opik;
27
+ };
28
+ type OpikExtension = {
29
+ /**
30
+ * Flush all pending traces and spans to Opik backend
31
+ */
32
+ flush: () => Promise<void>;
33
+ };
34
+
35
+ /**
36
+ * Track Gemini client with Opik observability
37
+ *
38
+ * Wraps a Gemini SDK instance (GoogleGenerativeAI or any Gemini client) to automatically
39
+ * create traces and spans for all generation calls.
40
+ *
41
+ * @param sdk - The Gemini SDK instance to track
42
+ * @param opikConfig - Configuration for Opik tracking
43
+ * @returns Proxied SDK instance with flush() method
44
+ *
45
+ */
46
+ declare const trackGemini: <SDKType extends object>(sdk: SDKType, opikConfig?: TrackOpikConfig) => SDKType & OpikExtension;
47
+
48
+ export { trackGemini };
@@ -0,0 +1,48 @@
1
+ import { Trace, Span, Opik } from 'opik';
2
+
3
+ type OpikParent = Trace | Span;
4
+ type TrackOpikConfig = {
5
+ /**
6
+ * Project name for this trace
7
+ */
8
+ projectName?: string;
9
+ /**
10
+ * Parent span or trace for this generation
11
+ */
12
+ parent?: OpikParent;
13
+ /**
14
+ * Generation name (defaults to "ClassName.methodName")
15
+ */
16
+ generationName?: string;
17
+ /**
18
+ * Trace metadata (tags, custom fields)
19
+ */
20
+ traceMetadata?: Record<string, unknown> & {
21
+ tags?: string[];
22
+ };
23
+ /**
24
+ * Opik client instance
25
+ */
26
+ client?: Opik;
27
+ };
28
+ type OpikExtension = {
29
+ /**
30
+ * Flush all pending traces and spans to Opik backend
31
+ */
32
+ flush: () => Promise<void>;
33
+ };
34
+
35
+ /**
36
+ * Track Gemini client with Opik observability
37
+ *
38
+ * Wraps a Gemini SDK instance (GoogleGenerativeAI or any Gemini client) to automatically
39
+ * create traces and spans for all generation calls.
40
+ *
41
+ * @param sdk - The Gemini SDK instance to track
42
+ * @param opikConfig - Configuration for Opik tracking
43
+ * @returns Proxied SDK instance with flush() method
44
+ *
45
+ */
46
+ declare const trackGemini: <SDKType extends object>(sdk: SDKType, opikConfig?: TrackOpikConfig) => SDKType & OpikExtension;
47
+
48
+ export { trackGemini };
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ import {Opik,OpikSpanType}from'opik';var R=class R{static getInstance(e){return R.instance||(R.instance=new Opik(e)),R.instance}};R.instance=null;var A=R;var x=a=>{if(a)return a.replace(/^models\//,"")};var G=a=>a!==null&&typeof a=="object"&&Symbol.asyncIterator in a&&typeof a[Symbol.asyncIterator]=="function",P=(a,e="")=>{let o={};for(let[n,t]of Object.entries(a)){let i=e?`${e}.${n}`:n;t&&typeof t=="object"&&!Array.isArray(t)&&!(t instanceof Date)?Object.assign(o,P(t,i)):o[i]=t;}return o};var E=a=>{let e=a.model;e&&(e=x(e));let o={};"contents"in a&&(o.contents=a.contents),"prompt"in a&&(o.prompt=a.prompt),"config"in a&&(o.config=a.config);let n={};if("config"in a&&typeof a.config=="object"&&a.config!==null){let t=a.config;"systemInstruction"in t&&(n.systemInstruction=t.systemInstruction),"generationConfig"in t&&(n.generationConfig=t.generationConfig),"safetySettings"in t&&(n.safetySettings=t.safetySettings),"tools"in t&&(n.tools=t.tools),"toolConfig"in t&&(n.toolConfig=t.toolConfig);let i=["temperature","maxOutputTokens","topP","topK","candidateCount","stopSequences"];for(let c of i)c in t&&(n[c]=t[c]);}return {model:e,input:o,modelParameters:n}},O=a=>{if(!a||typeof a!="object")return;let e=a;if("candidates"in e&&Array.isArray(e.candidates))return e.candidates.length===0?void 0:{candidates:e.candidates}},D=a=>{if(!a||typeof a!="object")return;let e=a,o="usageMetadata"in e&&e.usageMetadata||"usage_metadata"in e&&e.usage_metadata||"usage"in e&&e.usage;if(!o||typeof o!="object")return;let n=o,t={};(typeof n.promptTokenCount=="number"||typeof n.prompt_token_count=="number")&&(t.prompt_tokens=n.promptTokenCount||n.prompt_token_count),(typeof n.candidatesTokenCount=="number"||typeof n.candidates_token_count=="number")&&(t.completion_tokens=n.candidatesTokenCount||n.candidates_token_count),(typeof n.totalTokenCount=="number"||typeof n.total_token_count=="number")&&(t.total_tokens=n.totalTokenCount||n.total_token_count),(typeof n.cachedContentTokenCount=="number"||typeof n.cached_content_token_count=="number")&&(t.cached_content_tokens=n.cachedContentTokenCount||n.cached_content_token_count);let i={...n};delete i.promptTokensDetails,delete i.prompt_tokens_details,delete i.candidatesTokensDetails,delete i.candidates_tokens_details;let c=P(i,"original_usage");for(let[l,s]of Object.entries(c))typeof s=="number"&&(t[l]=s);return Object.keys(t).length>0?t:void 0},M=a=>{if(!a||typeof a!="object")return {isToolCall:false,data:""};let e=a;if("candidates"in e&&Array.isArray(e.candidates)&&e.candidates.length>0){let o=e.candidates[0];if(o&&"content"in o&&o.content&&typeof o.content=="object"){let n=o.content;if("parts"in n&&Array.isArray(n.parts)&&n.parts.length>0){let t=n.parts[0];if("functionCall"in t||"function_call"in t)return {isToolCall:true,data:JSON.stringify(t.functionCall||t.function_call)};if("text"in t&&typeof t.text=="string")return {isToolCall:false,data:t.text}}}}return {isToolCall:false,data:""}},C=a=>{if(!a||typeof a!="object")return {model:void 0,metadata:void 0};let e=a,o,n={};if("modelVersion"in e&&typeof e.modelVersion=="string"?o=x(e.modelVersion):"model_version"in e&&typeof e.model_version=="string"&&(o=x(e.model_version)),"candidates"in e&&Array.isArray(e.candidates)&&e.candidates.length>0){let t=e.candidates[0];"safetyRatings"in t?n.safety_ratings=t.safetyRatings:"safety_ratings"in t&&(n.safety_ratings=t.safety_ratings),"finishReason"in t?n.finish_reason=t.finishReason:"finish_reason"in t&&(n.finish_reason=t.finish_reason);}return "promptFeedback"in e?n.prompt_feedback=e.promptFeedback:"prompt_feedback"in e&&(n.prompt_feedback=e.prompt_feedback),n.created_from="genai",{model:o,metadata:Object.keys(n).length>0?n:void 0}};var v=(a,e)=>(...o)=>{var g;let{model:n,input:t,modelParameters:i}=E(o[0]),{tags:c=[],...l}=(g=e==null?void 0:e.traceMetadata)!=null?g:{},s={name:e.generationName,startTime:new Date,input:t,model:n,provider:e.provider,metadata:{...l,...i},tags:["genai",...c]},r,y=!!(e!=null&&e.parent);e!=null&&e.parent?r=e.parent:r=e.client.trace(s);try{let d=a(...o);if(G(d))return N(d,r,y,s);if(d instanceof Promise)return d.then(p=>{if(G(p))return N(p,r,y,s);let F=O(p),S=D(p),{model:$,metadata:K}=C(p),L=r.span({...s,output:F,endTime:new Date,usage:S,model:$||s.model,type:OpikSpanType.Llm,metadata:{...s.metadata,...K,usage:S}}),j=p;return j.functionCalls&&Array.isArray(j.functionCalls)&&j.functionCalls.forEach(h=>{L.span({name:`function_call: ${h.name}`,startTime:new Date,endTime:new Date,input:{arguments:h.args},output:{function_name:h.name},type:OpikSpanType.Tool,metadata:{function_name:h.name,function_arguments:h.args}});}),y||r.update({output:F,endTime:new Date}),p}).catch(p=>{throw I(p,r,s),p});let k=O(d),m=D(d),{model:b,metadata:f}=C(d),u=r.span({...s,output:k,endTime:new Date,usage:m,model:b||s.model,type:OpikSpanType.Llm,metadata:{...s.metadata,...f,usage:m}}),_=d;return _.functionCalls&&Array.isArray(_.functionCalls)&&_.functionCalls.forEach(w=>{u.span({name:`function_call: ${w.name}`,startTime:new Date,endTime:new Date,input:{arguments:w.args},output:{function_name:w.name},type:OpikSpanType.Tool,metadata:{function_name:w.name,function_arguments:w.args}});}),y||r.update({output:k,endTime:new Date}),d}catch(d){throw I(d,r,s),d}};function N(a,e,o,n){async function*t(){var y,g,d,k;let i=[],c=[],l,s={},r;try{for await(let f of a){i.push(f);let u=M(f);u.isToolCall||c.push(u.data),yield f;}let m=i[i.length-1];if(m){l=D(m),r=C(m);let f=c.join(""),u=O(m);if(u!=null&&u.candidates){s={candidates:structuredClone(u.candidates)};let _=s;(k=(d=(g=(y=_.candidates)==null?void 0:y[0])==null?void 0:g.content)==null?void 0:d.parts)!=null&&k[0]&&(_.candidates[0].content.parts[0].text=f);}}let b=e.span({...n,output:s,endTime:new Date,usage:l,model:(r==null?void 0:r.model)||n.model,type:OpikSpanType.Llm,metadata:{...n.metadata,...r==null?void 0:r.metadata,usage:l}});if(m&&typeof m=="object"){let f=m;f.functionCalls&&Array.isArray(f.functionCalls)&&f.functionCalls.forEach(u=>{b.span({name:`function_call: ${u.name}`,startTime:new Date,endTime:new Date,input:{arguments:u.args},output:{function_name:u.name},type:OpikSpanType.Tool,metadata:{function_name:u.name,function_arguments:u.args}});});}o||e.update({output:s,endTime:new Date});}catch(m){throw I(m,e,n),m}}return t()}var I=(a,e,o)=>{var n;e.span({...o,endTime:new Date,type:OpikSpanType.Llm,errorInfo:{message:a.message,exceptionType:a.name,traceback:(n=a.stack)!=null?n:""}}),e.end();};var V=a=>"vertexai"in a&&a.vertexai?"google_vertexai":"google_ai",z=(a,e)=>{let o=V(a);return new Proxy(a,{get(n,t,i){var g,d,k;let c=n[t],l=((g=a.constructor)==null?void 0:g.name)||"Gemini",r={generationName:(d=e==null?void 0:e.generationName)!=null?d:`${l}.${t.toString()}`,client:(k=e==null?void 0:e.client)!=null?k:A.getInstance(),parent:e==null?void 0:e.parent,traceMetadata:e==null?void 0:e.traceMetadata,provider:o};return t==="flush"?r.client.flush.bind(r.client):typeof c=="function"?v(c.bind(n),r):c&&!Array.isArray(c)&&!(c instanceof Date)&&typeof c=="object"?new Proxy(c,{get(m,b,f){let u=m[b];return typeof u=="function"?v(u.bind(m),r):Reflect.get(m,b,f)}}):Reflect.get(n,t,i)}})};
2
+ export{z as trackGemini};
package/package.json ADDED
@@ -0,0 +1,79 @@
1
+ {
2
+ "name": "opik-gemini",
3
+ "description": "Opik TypeScript and JavaScript SDK integration with Google Gemini AI",
4
+ "version": "0.1.1",
5
+ "engines": {
6
+ "node": ">=18"
7
+ },
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/comet-ml/opik.git",
11
+ "directory": "sdks/typescript/src/opik/integrations/opik-gemini"
12
+ },
13
+ "homepage": "https://www.comet.com/docs/opik/",
14
+ "author": {
15
+ "name": "Comet",
16
+ "email": "support@comet.com",
17
+ "url": "https://github.com/comet-ml"
18
+ },
19
+ "bugs": {
20
+ "url": "https://github.com/comet-ml/opik/issues",
21
+ "email": "support@comet.com"
22
+ },
23
+ "license": "Apache-2.0",
24
+ "keywords": [
25
+ "opik",
26
+ "gemini",
27
+ "google-ai",
28
+ "google-generative-ai",
29
+ "genai",
30
+ "gemini-integration",
31
+ "sdk",
32
+ "javascript",
33
+ "javascript-sdk",
34
+ "typescript",
35
+ "typescript-sdk",
36
+ "llm",
37
+ "tracing",
38
+ "observability",
39
+ "comet"
40
+ ],
41
+ "exports": {
42
+ "./package.json": "./package.json",
43
+ ".": {
44
+ "types": "./dist/index.d.ts",
45
+ "import": "./dist/index.js",
46
+ "require": "./dist/index.cjs"
47
+ }
48
+ },
49
+ "main": "dist/index.cjs",
50
+ "module": "dist/index.js",
51
+ "types": "dist/index.d.ts",
52
+ "type": "module",
53
+ "scripts": {
54
+ "build": "tsup",
55
+ "watch": "tsup --watch",
56
+ "lint": "eslint '**/*.{ts,tsx}'",
57
+ "typecheck": "tsc --noEmit",
58
+ "format": "prettier --write 'src/**/*.{ts,tsx,js,jsx,json,md}'",
59
+ "test": "vitest"
60
+ },
61
+ "files": [
62
+ "dist/**/*",
63
+ "README.md"
64
+ ],
65
+ "peerDependencies": {
66
+ "@google/genai": ">=1.0.0",
67
+ "opik": "^1.7.25"
68
+ },
69
+ "devDependencies": {
70
+ "@eslint/js": "^9.20.0",
71
+ "eslint": "^9.34.0",
72
+ "globals": "^15.14.0",
73
+ "prettier": "^3.6.2",
74
+ "tsup": "^8.5.0",
75
+ "typescript": "^5.9.2",
76
+ "typescript-eslint": "^8.42.0",
77
+ "vitest": "^3.0.5"
78
+ }
79
+ }