@tensorcost/sdk 0.3.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/CHANGELOG.md +36 -0
- package/LICENSE +201 -0
- package/README.md +119 -0
- package/dist/applied.d.ts +98 -0
- package/dist/applied.d.ts.map +1 -0
- package/dist/applied.js +174 -0
- package/dist/applied.js.map +1 -0
- package/dist/config.d.ts +29 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +86 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/providers/anthropic.d.ts +13 -0
- package/dist/providers/anthropic.d.ts.map +1 -0
- package/dist/providers/anthropic.js +119 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/detect.d.ts +13 -0
- package/dist/providers/detect.d.ts.map +1 -0
- package/dist/providers/detect.js +39 -0
- package/dist/providers/detect.js.map +1 -0
- package/dist/providers/index.d.ts +4 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +4 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/openai.d.ts +16 -0
- package/dist/providers/openai.d.ts.map +1 -0
- package/dist/providers/openai.js +137 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/transport.d.ts +56 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +150 -0
- package/dist/transport.js.map +1 -0
- package/dist/types.d.ts +143 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/dist/wrap.d.ts +16 -0
- package/dist/wrap.d.ts.map +1 -0
- package/dist/wrap.js +48 -0
- package/dist/wrap.js.map +1 -0
- package/package.json +48 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9D,eAAO,MAAM,gBAAgB,+BAA+B,CAAC;AAE7D,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAED,yEAAyE;AACzE,qBAAa,qBAAsB,SAAQ,KAAK;gBAClC,OAAO,EAAE,MAAM;CAI5B;AAED,wBAAgB,aAAa,CAAC,OAAO,GAAE,WAAgB,GAAG,cAAc,CAsEvE"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration resolution for the SDK.
|
|
3
|
+
*
|
|
4
|
+
* Resolution order:
|
|
5
|
+
* 1. Explicit options to wrap()
|
|
6
|
+
* 2. Environment variables (TENSORCOST_API_KEY, TENSORCOST_BASE_URL,
|
|
7
|
+
* TENSORCOST_TENANT_ID, TENSORCOST_PROXY_URL)
|
|
8
|
+
* 3. Defaults (only for baseUrl)
|
|
9
|
+
*
|
|
10
|
+
* If apiKey is still missing after resolution, MissingConfigError is
|
|
11
|
+
* raised. This is the *one* case the SDK does not fail open — it means
|
|
12
|
+
* the SDK was never configured at all, and silently dropping every
|
|
13
|
+
* observation would be worse than a loud import-time failure.
|
|
14
|
+
*
|
|
15
|
+
* If appliedMode=true and proxyUrl is missing from both options and env,
|
|
16
|
+
* TensorCostConfigError is thrown. Applied mode needs an explicit proxy
|
|
17
|
+
* URL; there is no safe default.
|
|
18
|
+
*/
|
|
19
|
+
export const DEFAULT_BASE_URL = "https://api.tensorcost.com";
|
|
20
|
+
export class MissingConfigError extends Error {
|
|
21
|
+
constructor(message) {
|
|
22
|
+
super(message);
|
|
23
|
+
this.name = "MissingConfigError";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/** Thrown when applied mode is enabled but required config is absent. */
|
|
27
|
+
export class TensorCostConfigError extends Error {
|
|
28
|
+
constructor(message) {
|
|
29
|
+
super(message);
|
|
30
|
+
this.name = "TensorCostConfigError";
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export function resolveConfig(options = {}) {
|
|
34
|
+
const apiKey = options.apiKey ?? process.env.TENSORCOST_API_KEY ?? "";
|
|
35
|
+
const baseUrlRaw = options.baseUrl ?? process.env.TENSORCOST_BASE_URL ?? DEFAULT_BASE_URL;
|
|
36
|
+
const tenantId = options.tenantId ?? process.env.TENSORCOST_TENANT_ID ?? null;
|
|
37
|
+
// Phase A4 batch 3 — environment tag. Trimmed; capped at 64 chars per
|
|
38
|
+
// project_environment_scoping.md. An empty / whitespace-only value
|
|
39
|
+
// collapses to null so the SDK omits the field from the envelope and
|
|
40
|
+
// the backend's column default ('production') applies.
|
|
41
|
+
const environmentRaw = options.environment ?? process.env.TENSORCOST_ENVIRONMENT ?? null;
|
|
42
|
+
let environment = null;
|
|
43
|
+
if (environmentRaw !== null) {
|
|
44
|
+
const trimmed = String(environmentRaw).trim();
|
|
45
|
+
if (trimmed.length > 64) {
|
|
46
|
+
throw new MissingConfigError("TensorCost environment must be 64 characters or fewer.");
|
|
47
|
+
}
|
|
48
|
+
environment = trimmed.length === 0 ? null : trimmed;
|
|
49
|
+
}
|
|
50
|
+
// Phase A4 batch 4 — provider-connection id. Same trim+cap rules as
|
|
51
|
+
// environment; collapsing blank to null so the SDK omits the field
|
|
52
|
+
// when not configured.
|
|
53
|
+
const connectionIdRaw = options.connectionId ?? process.env.TENSORCOST_CONNECTION_ID ?? null;
|
|
54
|
+
let connectionId = null;
|
|
55
|
+
if (connectionIdRaw !== null) {
|
|
56
|
+
const trimmed = String(connectionIdRaw).trim();
|
|
57
|
+
if (trimmed.length > 64) {
|
|
58
|
+
throw new MissingConfigError("TensorCost connectionId must be 64 characters or fewer.");
|
|
59
|
+
}
|
|
60
|
+
connectionId = trimmed.length === 0 ? null : trimmed;
|
|
61
|
+
}
|
|
62
|
+
const failOpen = options.failOpen ?? true;
|
|
63
|
+
const appliedMode = options.appliedMode ?? false;
|
|
64
|
+
const proxyUrlRaw = options.proxyUrl ?? process.env.TENSORCOST_PROXY_URL ?? null;
|
|
65
|
+
const proxyUrl = proxyUrlRaw !== null ? proxyUrlRaw.replace(/\/+$/, "") : null;
|
|
66
|
+
if (!apiKey) {
|
|
67
|
+
throw new MissingConfigError("TensorCost apiKey not found. Pass { apiKey } to wrap() or set the " +
|
|
68
|
+
"TENSORCOST_API_KEY environment variable.");
|
|
69
|
+
}
|
|
70
|
+
if (appliedMode && !proxyUrl) {
|
|
71
|
+
throw new TensorCostConfigError("TensorCost applied mode is enabled but no proxy URL was provided. " +
|
|
72
|
+
"Pass { proxyUrl } to wrap() or set the TENSORCOST_PROXY_URL " +
|
|
73
|
+
"environment variable.");
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
apiKey,
|
|
77
|
+
baseUrl: baseUrlRaw.replace(/\/+$/, ""),
|
|
78
|
+
tenantId: tenantId || null,
|
|
79
|
+
environment,
|
|
80
|
+
connectionId,
|
|
81
|
+
failOpen,
|
|
82
|
+
appliedMode,
|
|
83
|
+
proxyUrl,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { wrap } from "./wrap.js";
|
|
2
|
+
export { MissingConfigError, TensorCostConfigError } from "./config.js";
|
|
3
|
+
export { UnsupportedClientError } from "./providers/detect.js";
|
|
4
|
+
export type { WrapOptions, Observation, Provider } from "./types.js";
|
|
5
|
+
export declare const version = "0.3.0";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAErE,eAAO,MAAM,OAAO,UAAU,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAG/D,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anthropic client wrapping.
|
|
3
|
+
*
|
|
4
|
+
* Intercepts `client.messages.create`. Same observation contract as the
|
|
5
|
+
* OpenAI wrapper — only metadata is captured (model, input/output tokens),
|
|
6
|
+
* never the message content itself.
|
|
7
|
+
*/
|
|
8
|
+
import type { ObservationTransport } from "../transport.js";
|
|
9
|
+
export declare function install(client: unknown, transport: ObservationTransport, tenantId: string | null, environment: string | null, connectionId: string | null): void;
|
|
10
|
+
export declare const __test__: {
|
|
11
|
+
MARKER: string;
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=anthropic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../src/providers/anthropic.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AA8G5D,wBAAgB,OAAO,CACrB,MAAM,EAAE,OAAO,EACf,SAAS,EAAE,oBAAoB,EAC/B,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,GAC1B,IAAI,CAkBN;AAED,eAAO,MAAM,QAAQ;;CAAa,CAAC"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anthropic client wrapping.
|
|
3
|
+
*
|
|
4
|
+
* Intercepts `client.messages.create`. Same observation contract as the
|
|
5
|
+
* OpenAI wrapper — only metadata is captured (model, input/output tokens),
|
|
6
|
+
* never the message content itself.
|
|
7
|
+
*/
|
|
8
|
+
import { randomUUID } from "node:crypto";
|
|
9
|
+
const SDK_VERSION = "tensorcost-node/0.3.0";
|
|
10
|
+
const MARKER = "__tensorcost_wrapped__";
|
|
11
|
+
function nowIso() {
|
|
12
|
+
return new Date().toISOString();
|
|
13
|
+
}
|
|
14
|
+
function extractUsage(response) {
|
|
15
|
+
if (!response || typeof response !== "object") {
|
|
16
|
+
return { input: null, output: null };
|
|
17
|
+
}
|
|
18
|
+
const usage = response.usage;
|
|
19
|
+
if (!usage || typeof usage !== "object") {
|
|
20
|
+
return { input: null, output: null };
|
|
21
|
+
}
|
|
22
|
+
const u = usage;
|
|
23
|
+
const input = typeof u.input_tokens === "number" ? u.input_tokens : null;
|
|
24
|
+
const output = typeof u.output_tokens === "number" ? u.output_tokens : null;
|
|
25
|
+
return { input, output };
|
|
26
|
+
}
|
|
27
|
+
function wrapMethod(bound, opts) {
|
|
28
|
+
const wrapped = async function (...args) {
|
|
29
|
+
const requestAt = nowIso();
|
|
30
|
+
const firstArg = args[0];
|
|
31
|
+
const model = (firstArg && typeof firstArg === "object" && firstArg.model) ||
|
|
32
|
+
"unknown";
|
|
33
|
+
const correlationId = randomUUID();
|
|
34
|
+
try {
|
|
35
|
+
const response = await bound.apply(this, args);
|
|
36
|
+
const { input, output } = extractUsage(response);
|
|
37
|
+
const obs = {
|
|
38
|
+
sdk_version: SDK_VERSION,
|
|
39
|
+
provider: "anthropic",
|
|
40
|
+
model: String(model),
|
|
41
|
+
operation: "messages",
|
|
42
|
+
request_at: requestAt,
|
|
43
|
+
response_at: nowIso(),
|
|
44
|
+
input_tokens: input,
|
|
45
|
+
output_tokens: output,
|
|
46
|
+
cost_usd_cents: null,
|
|
47
|
+
status: "success",
|
|
48
|
+
error_message: null,
|
|
49
|
+
correlation_id: correlationId,
|
|
50
|
+
tenant_id: opts.tenantId,
|
|
51
|
+
...(opts.environment !== null
|
|
52
|
+
? { environment: opts.environment }
|
|
53
|
+
: {}),
|
|
54
|
+
...(opts.connectionId !== null
|
|
55
|
+
? { connection_id: opts.connectionId }
|
|
56
|
+
: {}),
|
|
57
|
+
};
|
|
58
|
+
try {
|
|
59
|
+
opts.transport.post(obs);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// transport already fail-open
|
|
63
|
+
}
|
|
64
|
+
return response;
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
const obs = {
|
|
68
|
+
sdk_version: SDK_VERSION,
|
|
69
|
+
provider: "anthropic",
|
|
70
|
+
model: String(model),
|
|
71
|
+
operation: "messages",
|
|
72
|
+
request_at: requestAt,
|
|
73
|
+
response_at: nowIso(),
|
|
74
|
+
input_tokens: null,
|
|
75
|
+
output_tokens: null,
|
|
76
|
+
cost_usd_cents: null,
|
|
77
|
+
status: "error",
|
|
78
|
+
error_message: `${err.name}: ${err.message}`,
|
|
79
|
+
correlation_id: correlationId,
|
|
80
|
+
tenant_id: opts.tenantId,
|
|
81
|
+
...(opts.environment !== null
|
|
82
|
+
? { environment: opts.environment }
|
|
83
|
+
: {}),
|
|
84
|
+
...(opts.connectionId !== null
|
|
85
|
+
? { connection_id: opts.connectionId }
|
|
86
|
+
: {}),
|
|
87
|
+
};
|
|
88
|
+
try {
|
|
89
|
+
opts.transport.post(obs);
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// see above
|
|
93
|
+
}
|
|
94
|
+
throw err;
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
Object.defineProperty(wrapped, MARKER, { value: true, enumerable: false });
|
|
98
|
+
return wrapped;
|
|
99
|
+
}
|
|
100
|
+
export function install(client, transport, tenantId, environment, connectionId) {
|
|
101
|
+
if (!client || typeof client !== "object")
|
|
102
|
+
return;
|
|
103
|
+
const c = client;
|
|
104
|
+
const messages = c.messages;
|
|
105
|
+
if (messages &&
|
|
106
|
+
typeof messages === "object" &&
|
|
107
|
+
typeof messages.create === "function" &&
|
|
108
|
+
!messages.create[MARKER]) {
|
|
109
|
+
const bound = messages.create.bind(messages);
|
|
110
|
+
messages.create = wrapMethod(bound, {
|
|
111
|
+
transport,
|
|
112
|
+
tenantId,
|
|
113
|
+
environment,
|
|
114
|
+
connectionId,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
export const __test__ = { MARKER };
|
|
119
|
+
//# sourceMappingURL=anthropic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../src/providers/anthropic.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider detection — duck-typed.
|
|
3
|
+
*
|
|
4
|
+
* We avoid importing `openai` or `@anthropic-ai/sdk` at runtime so that
|
|
5
|
+
* the SDK has zero peer-dep cost when the customer only uses one of the
|
|
6
|
+
* two providers. Detection inspects the shape of the client object.
|
|
7
|
+
*/
|
|
8
|
+
import type { Provider } from "../types.js";
|
|
9
|
+
export declare class UnsupportedClientError extends Error {
|
|
10
|
+
constructor(message: string);
|
|
11
|
+
}
|
|
12
|
+
export declare function detectProvider(client: unknown): Provider;
|
|
13
|
+
//# sourceMappingURL=detect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../../src/providers/detect.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,qBAAa,sBAAuB,SAAQ,KAAK;gBACnC,OAAO,EAAE,MAAM;CAI5B;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG,QAAQ,CAkCxD"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider detection — duck-typed.
|
|
3
|
+
*
|
|
4
|
+
* We avoid importing `openai` or `@anthropic-ai/sdk` at runtime so that
|
|
5
|
+
* the SDK has zero peer-dep cost when the customer only uses one of the
|
|
6
|
+
* two providers. Detection inspects the shape of the client object.
|
|
7
|
+
*/
|
|
8
|
+
export class UnsupportedClientError extends Error {
|
|
9
|
+
constructor(message) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = "UnsupportedClientError";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export function detectProvider(client) {
|
|
15
|
+
if (client === null || typeof client !== "object") {
|
|
16
|
+
throw new UnsupportedClientError("tensorcost: wrap() requires an object (an OpenAI or Anthropic client instance).");
|
|
17
|
+
}
|
|
18
|
+
const c = client;
|
|
19
|
+
// OpenAI: has chat.completions.create OR completions.create
|
|
20
|
+
const chat = c.chat;
|
|
21
|
+
const chatCompletions = chat && typeof chat === "object" ? chat.completions : undefined;
|
|
22
|
+
const hasOpenAIShape = (chatCompletions && typeof chatCompletions.create === "function") ||
|
|
23
|
+
(c.completions && typeof c.completions.create === "function");
|
|
24
|
+
// Anthropic: has messages.create
|
|
25
|
+
const messages = c.messages;
|
|
26
|
+
const hasAnthropicShape = messages && typeof messages.create === "function";
|
|
27
|
+
// Disambiguation: OpenAI also has a top-level `completions` AND `chat`.
|
|
28
|
+
// Anthropic only exposes `messages`. If both shapes match (unusual),
|
|
29
|
+
// prefer the more specific Anthropic match only when there's no chat.
|
|
30
|
+
if (hasOpenAIShape) {
|
|
31
|
+
return "openai";
|
|
32
|
+
}
|
|
33
|
+
if (hasAnthropicShape) {
|
|
34
|
+
return "anthropic";
|
|
35
|
+
}
|
|
36
|
+
throw new UnsupportedClientError("tensorcost: client does not look like an OpenAI or Anthropic instance " +
|
|
37
|
+
"(missing chat.completions.create / completions.create / messages.create).");
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=detect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect.js","sourceRoot":"","sources":["../../src/providers/detect.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,OAAO,sBAAuB,SAAQ,KAAK;IAC/C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC;IACvC,CAAC;CACF;AAED,MAAM,UAAU,cAAc,CAAC,MAAe;IAC5C,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QAClD,MAAM,IAAI,sBAAsB,CAC9B,iFAAiF,CAClF,CAAC;IACJ,CAAC;IACD,MAAM,CAAC,GAAG,MAA6B,CAAC;IAExC,4DAA4D;IAC5D,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC;IACpB,MAAM,eAAe,GACnB,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC;IAClE,MAAM,cAAc,GAClB,CAAC,eAAe,IAAI,OAAO,eAAe,CAAC,MAAM,KAAK,UAAU,CAAC;QACjE,CAAC,CAAC,CAAC,WAAW,IAAI,OAAO,CAAC,CAAC,WAAW,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC;IAEhE,iCAAiC;IACjC,MAAM,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC;IAC5B,MAAM,iBAAiB,GACrB,QAAQ,IAAI,OAAO,QAAQ,CAAC,MAAM,KAAK,UAAU,CAAC;IAEpD,wEAAwE;IACxE,qEAAqE;IACrE,sEAAsE;IACtE,IAAI,cAAc,EAAE,CAAC;QACnB,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,IAAI,iBAAiB,EAAE,CAAC;QACtB,OAAO,WAAW,CAAC;IACrB,CAAC;IACD,MAAM,IAAI,sBAAsB,CAC9B,wEAAwE;QACtE,2EAA2E,CAC9E,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,KAAK,cAAc,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,iBAAiB,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,KAAK,cAAc,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,iBAAiB,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI client wrapping.
|
|
3
|
+
*
|
|
4
|
+
* Intercepts:
|
|
5
|
+
* * `client.chat.completions.create`
|
|
6
|
+
* * `client.completions.create` (legacy text completions; v0 still ships)
|
|
7
|
+
*
|
|
8
|
+
* We capture only metadata (model, token usage) — never prompt content.
|
|
9
|
+
* Customer prompts stay on their machine.
|
|
10
|
+
*/
|
|
11
|
+
import type { ObservationTransport } from "../transport.js";
|
|
12
|
+
export declare function install(client: unknown, transport: ObservationTransport, tenantId: string | null, environment: string | null, connectionId: string | null): void;
|
|
13
|
+
export declare const __test__: {
|
|
14
|
+
MARKER: string;
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=openai.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/providers/openai.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAuH5D,wBAAgB,OAAO,CACrB,MAAM,EAAE,OAAO,EACf,SAAS,EAAE,oBAAoB,EAC/B,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,GAC1B,IAAI,CAsCN;AAED,eAAO,MAAM,QAAQ;;CAAa,CAAC"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI client wrapping.
|
|
3
|
+
*
|
|
4
|
+
* Intercepts:
|
|
5
|
+
* * `client.chat.completions.create`
|
|
6
|
+
* * `client.completions.create` (legacy text completions; v0 still ships)
|
|
7
|
+
*
|
|
8
|
+
* We capture only metadata (model, token usage) — never prompt content.
|
|
9
|
+
* Customer prompts stay on their machine.
|
|
10
|
+
*/
|
|
11
|
+
import { randomUUID } from "node:crypto";
|
|
12
|
+
const SDK_VERSION = "tensorcost-node/0.3.0";
|
|
13
|
+
const MARKER = "__tensorcost_wrapped__";
|
|
14
|
+
function nowIso() {
|
|
15
|
+
return new Date().toISOString();
|
|
16
|
+
}
|
|
17
|
+
function extractUsage(response) {
|
|
18
|
+
if (!response || typeof response !== "object") {
|
|
19
|
+
return { input: null, output: null };
|
|
20
|
+
}
|
|
21
|
+
const usage = response.usage;
|
|
22
|
+
if (!usage || typeof usage !== "object") {
|
|
23
|
+
return { input: null, output: null };
|
|
24
|
+
}
|
|
25
|
+
const u = usage;
|
|
26
|
+
const input = typeof u.prompt_tokens === "number" ? u.prompt_tokens : null;
|
|
27
|
+
const output = typeof u.completion_tokens === "number" ? u.completion_tokens : null;
|
|
28
|
+
return { input, output };
|
|
29
|
+
}
|
|
30
|
+
function wrapMethod(bound, opts) {
|
|
31
|
+
const wrapped = async function (...args) {
|
|
32
|
+
const requestAt = nowIso();
|
|
33
|
+
const firstArg = args[0];
|
|
34
|
+
const model = (firstArg && typeof firstArg === "object" && firstArg.model) ||
|
|
35
|
+
"unknown";
|
|
36
|
+
const correlationId = randomUUID();
|
|
37
|
+
try {
|
|
38
|
+
const response = await bound.apply(this, args);
|
|
39
|
+
const { input, output } = extractUsage(response);
|
|
40
|
+
const obs = {
|
|
41
|
+
sdk_version: SDK_VERSION,
|
|
42
|
+
provider: "openai",
|
|
43
|
+
model: String(model),
|
|
44
|
+
operation: opts.operation,
|
|
45
|
+
request_at: requestAt,
|
|
46
|
+
response_at: nowIso(),
|
|
47
|
+
input_tokens: input,
|
|
48
|
+
output_tokens: output,
|
|
49
|
+
cost_usd_cents: null,
|
|
50
|
+
status: "success",
|
|
51
|
+
error_message: null,
|
|
52
|
+
correlation_id: correlationId,
|
|
53
|
+
tenant_id: opts.tenantId,
|
|
54
|
+
...(opts.environment !== null
|
|
55
|
+
? { environment: opts.environment }
|
|
56
|
+
: {}),
|
|
57
|
+
...(opts.connectionId !== null
|
|
58
|
+
? { connection_id: opts.connectionId }
|
|
59
|
+
: {}),
|
|
60
|
+
};
|
|
61
|
+
try {
|
|
62
|
+
opts.transport.post(obs);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// belt-and-suspenders: transport itself is fail-open
|
|
66
|
+
}
|
|
67
|
+
return response;
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
const obs = {
|
|
71
|
+
sdk_version: SDK_VERSION,
|
|
72
|
+
provider: "openai",
|
|
73
|
+
model: String(model),
|
|
74
|
+
operation: opts.operation,
|
|
75
|
+
request_at: requestAt,
|
|
76
|
+
response_at: nowIso(),
|
|
77
|
+
input_tokens: null,
|
|
78
|
+
output_tokens: null,
|
|
79
|
+
cost_usd_cents: null,
|
|
80
|
+
status: "error",
|
|
81
|
+
error_message: `${err.name}: ${err.message}`,
|
|
82
|
+
correlation_id: correlationId,
|
|
83
|
+
tenant_id: opts.tenantId,
|
|
84
|
+
...(opts.environment !== null
|
|
85
|
+
? { environment: opts.environment }
|
|
86
|
+
: {}),
|
|
87
|
+
...(opts.connectionId !== null
|
|
88
|
+
? { connection_id: opts.connectionId }
|
|
89
|
+
: {}),
|
|
90
|
+
};
|
|
91
|
+
try {
|
|
92
|
+
opts.transport.post(obs);
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
// see above
|
|
96
|
+
}
|
|
97
|
+
throw err;
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
Object.defineProperty(wrapped, MARKER, { value: true, enumerable: false });
|
|
101
|
+
return wrapped;
|
|
102
|
+
}
|
|
103
|
+
export function install(client, transport, tenantId, environment, connectionId) {
|
|
104
|
+
if (!client || typeof client !== "object")
|
|
105
|
+
return;
|
|
106
|
+
const c = client;
|
|
107
|
+
const chat = c.chat;
|
|
108
|
+
const chatCompletions = chat && typeof chat === "object" ? chat.completions : undefined;
|
|
109
|
+
if (chatCompletions &&
|
|
110
|
+
typeof chatCompletions.create === "function" &&
|
|
111
|
+
!chatCompletions.create[MARKER]) {
|
|
112
|
+
const bound = chatCompletions.create.bind(chatCompletions);
|
|
113
|
+
chatCompletions.create = wrapMethod(bound, {
|
|
114
|
+
operation: "chat.completions",
|
|
115
|
+
transport,
|
|
116
|
+
tenantId,
|
|
117
|
+
environment,
|
|
118
|
+
connectionId,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
const legacy = c.completions;
|
|
122
|
+
if (legacy &&
|
|
123
|
+
typeof legacy === "object" &&
|
|
124
|
+
typeof legacy.create === "function" &&
|
|
125
|
+
!legacy.create[MARKER]) {
|
|
126
|
+
const bound = legacy.create.bind(legacy);
|
|
127
|
+
legacy.create = wrapMethod(bound, {
|
|
128
|
+
operation: "completions",
|
|
129
|
+
transport,
|
|
130
|
+
tenantId,
|
|
131
|
+
environment,
|
|
132
|
+
connectionId,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
export const __test__ = { MARKER };
|
|
137
|
+
//# sourceMappingURL=openai.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai.js","sourceRoot":"","sources":["../../src/providers/openai.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fire-and-forget HTTP transport for observation events.
|
|
3
|
+
*
|
|
4
|
+
* Design:
|
|
5
|
+
*
|
|
6
|
+
* * `post(observation)` returns synchronously the moment the observation
|
|
7
|
+
* is enqueued. The actual fetch runs on the microtask queue via
|
|
8
|
+
* `queueMicrotask`, so the customer's hot path is never blocked on
|
|
9
|
+
* network IO.
|
|
10
|
+
* * If the in-flight queue saturates (>= MAX_QUEUE_DEPTH), we drop the
|
|
11
|
+
* oldest observation rather than block. Losing one observation is
|
|
12
|
+
* strictly preferable to slowing down the customer's request loop.
|
|
13
|
+
* * A short-lived JWT is fetched once via
|
|
14
|
+
* `POST /api/inference-proxy/sdk-token/exchange` using the long-lived
|
|
15
|
+
* API key.
|
|
16
|
+
* The token is cached in-memory and refreshed when within
|
|
17
|
+
* TOKEN_REFRESH_LEEWAY_MS of expiry.
|
|
18
|
+
* * All errors during posting are swallowed when `failOpen` is true
|
|
19
|
+
* (the default) and surfaced via console.warn. The customer's call
|
|
20
|
+
* site never sees an exception.
|
|
21
|
+
*/
|
|
22
|
+
import type { Observation } from "./types.js";
|
|
23
|
+
export interface TransportOptions {
|
|
24
|
+
baseUrl: string;
|
|
25
|
+
apiKey: string;
|
|
26
|
+
timeoutMs?: number;
|
|
27
|
+
failOpen?: boolean;
|
|
28
|
+
/** Optional fetch override for testing. Defaults to global fetch. */
|
|
29
|
+
fetchImpl?: typeof fetch;
|
|
30
|
+
}
|
|
31
|
+
export declare class ObservationTransport {
|
|
32
|
+
private readonly baseUrl;
|
|
33
|
+
private readonly apiKey;
|
|
34
|
+
private readonly timeoutMs;
|
|
35
|
+
private readonly failOpen;
|
|
36
|
+
private readonly fetchImpl;
|
|
37
|
+
private pending;
|
|
38
|
+
private tokenCache;
|
|
39
|
+
/** In-flight token-exchange promise; coalesces concurrent refreshes. */
|
|
40
|
+
private tokenInflight;
|
|
41
|
+
constructor(opts: TransportOptions);
|
|
42
|
+
/**
|
|
43
|
+
* Enqueue an observation for fire-and-forget delivery. Returns
|
|
44
|
+
* immediately (synchronously). Errors are logged via console.warn,
|
|
45
|
+
* never thrown, when failOpen is true.
|
|
46
|
+
*/
|
|
47
|
+
post(observation: Observation): void;
|
|
48
|
+
private deliver;
|
|
49
|
+
private getToken;
|
|
50
|
+
private exchangeToken;
|
|
51
|
+
}
|
|
52
|
+
export declare const __test__: {
|
|
53
|
+
SDK_VERSION: string;
|
|
54
|
+
MAX_QUEUE_DEPTH: number;
|
|
55
|
+
};
|
|
56
|
+
//# sourceMappingURL=transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAQ9C,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qEAAqE;IACrE,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAOD,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAU;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IAEzC,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,UAAU,CAA2B;IAC7C,wEAAwE;IACxE,OAAO,CAAC,aAAa,CAAgC;gBAEzC,IAAI,EAAE,gBAAgB;IAQlC;;;;OAIG;IACH,IAAI,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI;YAqBtB,OAAO;YAqCP,QAAQ;YAiBR,aAAa;CAuC5B;AAED,eAAO,MAAM,QAAQ;;;CAGpB,CAAC"}
|