@aispendguard/sdk 0.1.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.
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Wraps a Google Gemini GenerativeModel so every generateContent() call is
3
+ * automatically tracked. Returns the original response unchanged.
4
+ *
5
+ * Usage:
6
+ * import { init, wrapGemini } from "@aispendguard/sdk";
7
+ * import { GoogleGenerativeAI } from "@google/generative-ai";
8
+ *
9
+ * init({ apiKey: "asg_...", defaultTags: { feature: "chat", route: "/api/chat" } });
10
+ * const genAI = new GoogleGenerativeAI(process.env.GOOGLE_AI_API_KEY!);
11
+ * const model = wrapGemini(genAI.getGenerativeModel({ model: "gemini-2.0-flash" }), "gemini-2.0-flash");
12
+ *
13
+ * // Automatically tracked:
14
+ * const result = await model.generateContent("Hello");
15
+ */
16
+ export declare function wrapGemini<T extends object>(client: T, modelName: string): T;
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.wrapGemini = wrapGemini;
4
+ const gemini_1 = require("./gemini");
5
+ const index_1 = require("./index");
6
+ /**
7
+ * Wraps a Google Gemini GenerativeModel so every generateContent() call is
8
+ * automatically tracked. Returns the original response unchanged.
9
+ *
10
+ * Usage:
11
+ * import { init, wrapGemini } from "@aispendguard/sdk";
12
+ * import { GoogleGenerativeAI } from "@google/generative-ai";
13
+ *
14
+ * init({ apiKey: "asg_...", defaultTags: { feature: "chat", route: "/api/chat" } });
15
+ * const genAI = new GoogleGenerativeAI(process.env.GOOGLE_AI_API_KEY!);
16
+ * const model = wrapGemini(genAI.getGenerativeModel({ model: "gemini-2.0-flash" }), "gemini-2.0-flash");
17
+ *
18
+ * // Automatically tracked:
19
+ * const result = await model.generateContent("Hello");
20
+ */
21
+ function wrapGemini(client, modelName) {
22
+ const originalGenerate = client.generateContent;
23
+ if (typeof originalGenerate !== "function")
24
+ return client;
25
+ client.generateContent = async function wrappedGenerate(...args) {
26
+ const start = Date.now();
27
+ const result = await originalGenerate.apply(this, args);
28
+ const latencyMs = Date.now() - start;
29
+ try {
30
+ const client = getClientSafe();
31
+ if (!client)
32
+ return result;
33
+ const res = result;
34
+ const response = res.response;
35
+ const usage = (response?.usageMetadata ?? res.usageMetadata);
36
+ const resolvedModel = typeof response?.modelVersion === "string"
37
+ ? response.modelVersion
38
+ : undefined;
39
+ // Check for asgTags in the first argument if it's an object
40
+ let asgTags;
41
+ if (args[0] && typeof args[0] === "object" && !Array.isArray(args[0])) {
42
+ asgTags = args[0].asgTags;
43
+ }
44
+ const tags = mergeTags(client.defaultTags, asgTags, modelName);
45
+ const event = (0, gemini_1.createGeminiUsageEvent)({
46
+ model: modelName,
47
+ resolvedModel,
48
+ usage: usage,
49
+ latencyMs,
50
+ tags,
51
+ });
52
+ client.trackUsage(event).catch(logError);
53
+ }
54
+ catch {
55
+ // Never break user code
56
+ }
57
+ return result;
58
+ };
59
+ return client;
60
+ }
61
+ function mergeTags(defaults, overrides, model) {
62
+ return {
63
+ task_type: "chat",
64
+ feature: "default",
65
+ route: "default",
66
+ ...defaults,
67
+ ...overrides,
68
+ source: "auto-wrap",
69
+ model,
70
+ };
71
+ }
72
+ function getClientSafe() {
73
+ try {
74
+ return (0, index_1.getClient)();
75
+ }
76
+ catch {
77
+ return null;
78
+ }
79
+ }
80
+ function logError(err) {
81
+ console.warn(`[aispendguard-sdk] auto-wrap tracking failed: ${err instanceof Error ? err.message : String(err)}`);
82
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Wraps an OpenAI client so every chat.completions.create() call is
3
+ * automatically tracked. Returns the original response unchanged.
4
+ *
5
+ * Usage:
6
+ * import { init, wrapOpenAI } from "@aispendguard/sdk";
7
+ * import OpenAI from "openai";
8
+ *
9
+ * init({ apiKey: "asg_...", defaultTags: { feature: "chat", route: "/api/chat" } });
10
+ * const openai = wrapOpenAI(new OpenAI());
11
+ *
12
+ * // Automatically tracked:
13
+ * const res = await openai.chat.completions.create({ model: "gpt-4o", messages: [...] });
14
+ */
15
+ export declare function wrapOpenAI<T extends object>(client: T): T;
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.wrapOpenAI = wrapOpenAI;
4
+ const openai_1 = require("./openai");
5
+ const index_1 = require("./index");
6
+ /**
7
+ * Wraps an OpenAI client so every chat.completions.create() call is
8
+ * automatically tracked. Returns the original response unchanged.
9
+ *
10
+ * Usage:
11
+ * import { init, wrapOpenAI } from "@aispendguard/sdk";
12
+ * import OpenAI from "openai";
13
+ *
14
+ * init({ apiKey: "asg_...", defaultTags: { feature: "chat", route: "/api/chat" } });
15
+ * const openai = wrapOpenAI(new OpenAI());
16
+ *
17
+ * // Automatically tracked:
18
+ * const res = await openai.chat.completions.create({ model: "gpt-4o", messages: [...] });
19
+ */
20
+ function wrapOpenAI(client) {
21
+ const chat = client.chat;
22
+ if (!chat || typeof chat !== "object")
23
+ return client;
24
+ const completions = chat.completions;
25
+ if (!completions || typeof completions !== "object")
26
+ return client;
27
+ const originalCreate = completions.create;
28
+ if (typeof originalCreate !== "function")
29
+ return client;
30
+ completions.create = async function wrappedCreate(params, ...rest) {
31
+ const start = Date.now();
32
+ const result = await originalCreate.call(this, params, ...rest);
33
+ const latencyMs = Date.now() - start;
34
+ try {
35
+ const client = getClientSafe();
36
+ if (!client)
37
+ return result;
38
+ const res = result;
39
+ const model = params.model ?? "unknown";
40
+ const resolvedModel = typeof res.model === "string" ? res.model : undefined;
41
+ const usage = res.usage;
42
+ const asgOpts = params;
43
+ const tags = mergeTags(client.defaultTags, asgOpts.asgTags, model);
44
+ const event = (0, openai_1.createOpenAIUsageEvent)({
45
+ model,
46
+ resolvedModel,
47
+ usage: usage,
48
+ latencyMs,
49
+ tags,
50
+ });
51
+ client.trackUsage(event).catch(logError);
52
+ }
53
+ catch {
54
+ // Never break user code
55
+ }
56
+ return result;
57
+ };
58
+ return client;
59
+ }
60
+ function mergeTags(defaults, overrides, model) {
61
+ return {
62
+ task_type: "chat",
63
+ feature: "default",
64
+ route: "default",
65
+ ...defaults,
66
+ ...overrides,
67
+ source: "auto-wrap",
68
+ model,
69
+ };
70
+ }
71
+ function getClientSafe() {
72
+ try {
73
+ return (0, index_1.getClient)();
74
+ }
75
+ catch {
76
+ return null;
77
+ }
78
+ }
79
+ function logError(err) {
80
+ console.warn(`[aispendguard-sdk] auto-wrap tracking failed: ${err instanceof Error ? err.message : String(err)}`);
81
+ }
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@aispendguard/sdk",
3
+ "version": "0.1.0",
4
+ "description": "Tags-only SDK for tracking AI API spend with AISpendGuard. Supports OpenAI, Anthropic, Google Gemini, LangChain.js, and OpenTelemetry.",
5
+ "license": "MIT",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist",
10
+ "LICENSE",
11
+ "README.md"
12
+ ],
13
+ "keywords": [
14
+ "ai",
15
+ "llm",
16
+ "cost",
17
+ "spending",
18
+ "tracking",
19
+ "openai",
20
+ "anthropic",
21
+ "gemini",
22
+ "langchain",
23
+ "observability"
24
+ ],
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/AISpendGuard/aispendguard-sdk.git"
28
+ },
29
+ "homepage": "https://aispendguard.com",
30
+ "bugs": {
31
+ "url": "https://github.com/AISpendGuard/aispendguard-sdk/issues"
32
+ },
33
+ "publishConfig": {
34
+ "access": "public"
35
+ },
36
+ "engines": {
37
+ "node": ">=18"
38
+ },
39
+ "scripts": {
40
+ "build": "tsc -p tsconfig.json",
41
+ "typecheck": "tsc -p tsconfig.json --noEmit",
42
+ "test": "npm run build && node --test tests/*.test.mjs",
43
+ "prepublishOnly": "npm run test"
44
+ },
45
+ "devDependencies": {
46
+ "@types/node": "^22.13.10",
47
+ "typescript": "^5.8.2"
48
+ }
49
+ }