@fluxgate/anthropic 0.0.1 → 0.0.2
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/dist/index.d.ts +4 -3
- package/dist/index.test.js +2 -3
- package/dist/types/types.d.ts +33 -2
- package/dist/utils/extractUsage.d.ts +2 -2
- package/dist/utils/extractUsage.js +6 -10
- package/dist/utils/extractUsage.test.js +20 -32
- package/dist/utils/recordUsage.d.ts +4 -3
- package/dist/utils/recordUsage.js +37 -26
- package/dist/wrappers/betaMessages.d.ts +8 -0
- package/dist/wrappers/betaMessages.js +70 -0
- package/dist/wrappers/completions.d.ts +8 -0
- package/dist/wrappers/completions.js +70 -0
- package/dist/wrappers/createWrappedClient.d.ts +4 -3
- package/dist/wrappers/createWrappedClient.js +7 -0
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FluxGate } from "@fluxgate/sdk";
|
|
2
2
|
import type Anthropic from "@anthropic-ai/sdk";
|
|
3
3
|
import { TrackedAnthropic } from "./types/types.js";
|
|
4
|
+
import { FluxGateContext } from "./types/types.js";
|
|
4
5
|
export type AnthropicTracker = {
|
|
5
|
-
withContext: (ctx:
|
|
6
|
+
withContext: (ctx: FluxGateContext) => TrackedAnthropic;
|
|
6
7
|
get client(): TrackedAnthropic;
|
|
7
8
|
};
|
|
8
9
|
export declare function createAnthropicCostTracker(client: Anthropic, instance: FluxGate): AnthropicTracker;
|
|
9
|
-
export type { AiEventMetadata, TrackedUser, FluxGateCostTrackingResponse, WithTracking, AiEventStatus, } from "@fluxgate/sdk";
|
|
10
|
+
export type { AiEventMetadata, TrackedUser, FluxGateCostTrackingResponse, WithTracking, AiEventStatus, Performance, CostOverride, } from "@fluxgate/sdk";
|
|
10
11
|
export * from "./types/types.js";
|
|
11
12
|
export { TrackedStream } from "./wrappers/TrackedStream.js";
|
package/dist/index.test.js
CHANGED
|
@@ -56,7 +56,7 @@ describe("createAnthropicCostTracker", () => {
|
|
|
56
56
|
id: "user-123",
|
|
57
57
|
name: "Test User",
|
|
58
58
|
email: "test@example.com",
|
|
59
|
-
monthlyRevenue: 99.99,
|
|
59
|
+
monthlyRevenue: "99.99",
|
|
60
60
|
},
|
|
61
61
|
conversationId: "conv-789",
|
|
62
62
|
});
|
|
@@ -67,8 +67,7 @@ describe("createAnthropicCostTracker", () => {
|
|
|
67
67
|
const contextClient = tracker.withContext({
|
|
68
68
|
feature: "chat",
|
|
69
69
|
step: "generation",
|
|
70
|
-
|
|
71
|
-
anotherField: 123,
|
|
70
|
+
region: "us-east-1",
|
|
72
71
|
});
|
|
73
72
|
expect(contextClient).toBeDefined();
|
|
74
73
|
});
|
package/dist/types/types.d.ts
CHANGED
|
@@ -1,13 +1,44 @@
|
|
|
1
1
|
import type Anthropic from "@anthropic-ai/sdk";
|
|
2
2
|
import type { Message, MessageCreateParams, MessageCreateParamsNonStreaming, MessageCreateParamsStreaming, RawMessageStreamEvent } from "@anthropic-ai/sdk/resources/messages";
|
|
3
|
-
import type {
|
|
3
|
+
import type { Completion, CompletionCreateParams, CompletionCreateParamsNonStreaming, CompletionCreateParamsStreaming } from "@anthropic-ai/sdk/resources/completions";
|
|
4
|
+
import type { BetaMessage, BetaRawMessageStreamEvent, MessageCreateParams as BetaMessageCreateParams, MessageCreateParamsNonStreaming as BetaMessageCreateParamsNonStreaming, MessageCreateParamsStreaming as BetaMessageCreateParamsStreaming } from "@anthropic-ai/sdk/resources/beta/messages/messages";
|
|
5
|
+
import type { WithTracking, TrackedUser, AiEventMetadata, CostOverride } from "@fluxgate/sdk";
|
|
4
6
|
import type { TrackedStream } from "../wrappers/TrackedStream.js";
|
|
5
7
|
type MessageCreateOptions = Parameters<Anthropic["messages"]["create"]>[1];
|
|
6
|
-
|
|
8
|
+
type CompletionCreateOptions = Parameters<Anthropic["completions"]["create"]>[1];
|
|
9
|
+
type BetaMessageCreateOptions = Parameters<Anthropic["beta"]["messages"]["create"]>[1];
|
|
10
|
+
export type TrackedAnthropic = Omit<Anthropic, "messages" | "completions" | "beta"> & {
|
|
7
11
|
messages: Omit<Anthropic["messages"], "create"> & {
|
|
8
12
|
create(body: MessageCreateParamsNonStreaming, options?: MessageCreateOptions): Promise<WithTracking<Message>>;
|
|
9
13
|
create(body: MessageCreateParamsStreaming, options?: MessageCreateOptions): Promise<TrackedStream<RawMessageStreamEvent>>;
|
|
10
14
|
create(body: MessageCreateParams, options?: MessageCreateOptions): Promise<WithTracking<Message> | TrackedStream<RawMessageStreamEvent>>;
|
|
11
15
|
};
|
|
16
|
+
completions: Omit<Anthropic["completions"], "create"> & {
|
|
17
|
+
create(body: CompletionCreateParamsNonStreaming, options?: CompletionCreateOptions): Promise<WithTracking<Completion>>;
|
|
18
|
+
create(body: CompletionCreateParamsStreaming, options?: CompletionCreateOptions): Promise<TrackedStream<Completion>>;
|
|
19
|
+
create(body: CompletionCreateParams, options?: CompletionCreateOptions): Promise<WithTracking<Completion> | TrackedStream<Completion>>;
|
|
20
|
+
};
|
|
21
|
+
beta: Omit<Anthropic["beta"], "messages"> & {
|
|
22
|
+
messages: Omit<Anthropic["beta"]["messages"], "create"> & {
|
|
23
|
+
create(params: BetaMessageCreateParamsNonStreaming, options?: BetaMessageCreateOptions): Promise<WithTracking<BetaMessage>>;
|
|
24
|
+
create(params: BetaMessageCreateParamsStreaming, options?: BetaMessageCreateOptions): Promise<TrackedStream<BetaRawMessageStreamEvent>>;
|
|
25
|
+
create(params: BetaMessageCreateParams, options?: BetaMessageCreateOptions): Promise<WithTracking<BetaMessage> | TrackedStream<BetaRawMessageStreamEvent>>;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
export type FluxGateContext = {
|
|
30
|
+
user?: string | TrackedUser;
|
|
31
|
+
feature?: string;
|
|
32
|
+
step?: string;
|
|
33
|
+
sessionId?: string;
|
|
34
|
+
conversationId?: string;
|
|
35
|
+
timestamp?: number;
|
|
36
|
+
serviceTier?: AiEventMetadata["serviceTier"];
|
|
37
|
+
region?: string;
|
|
38
|
+
openrouterCost?: number;
|
|
39
|
+
cacheTtl?: string;
|
|
40
|
+
costOverride?: CostOverride;
|
|
41
|
+
/** Arbitrary key-value pairs forwarded to the metadata object (e.g. { language: "typescript", documentType: "article" }) */
|
|
42
|
+
metadata?: Record<string, unknown>;
|
|
12
43
|
};
|
|
13
44
|
export {};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AiEventUsage } from "@fluxgate/sdk";
|
|
2
2
|
type AnthropicUsage = {
|
|
3
3
|
input_tokens: number | null;
|
|
4
4
|
output_tokens: number | null;
|
|
5
5
|
cache_creation_input_tokens?: number | null;
|
|
6
6
|
cache_read_input_tokens?: number | null;
|
|
7
7
|
};
|
|
8
|
-
export declare function extractAnthropicUsage(usage: AnthropicUsage | null | undefined):
|
|
8
|
+
export declare function extractAnthropicUsage(usage: AnthropicUsage | null | undefined): AiEventUsage;
|
|
9
9
|
export {};
|
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
export function extractAnthropicUsage(usage) {
|
|
2
2
|
if (!usage) {
|
|
3
3
|
return {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
cachedTokens: 0,
|
|
7
|
-
totalTokens: 0,
|
|
4
|
+
promptTokens: 0,
|
|
5
|
+
completionTokens: 0,
|
|
8
6
|
};
|
|
9
7
|
}
|
|
10
|
-
const cachedTokens = (usage.cache_creation_input_tokens ?? 0) +
|
|
11
|
-
(usage.cache_read_input_tokens ?? 0);
|
|
12
8
|
return {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
promptTokens: usage.input_tokens ?? 0,
|
|
10
|
+
completionTokens: usage.output_tokens ?? 0,
|
|
11
|
+
cacheWriteTokens: usage.cache_creation_input_tokens || undefined,
|
|
12
|
+
cacheReadTokens: usage.cache_read_input_tokens || undefined,
|
|
17
13
|
};
|
|
18
14
|
}
|
|
@@ -11,10 +11,10 @@ describe("extractAnthropicUsage", () => {
|
|
|
11
11
|
};
|
|
12
12
|
const result = extractAnthropicUsage(usage);
|
|
13
13
|
expect(result).toEqual({
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
promptTokens: 100,
|
|
15
|
+
completionTokens: 50,
|
|
16
|
+
cacheWriteTokens: 20,
|
|
17
|
+
cacheReadTokens: 10,
|
|
18
18
|
});
|
|
19
19
|
});
|
|
20
20
|
it("should handle missing cached token fields", () => {
|
|
@@ -24,10 +24,8 @@ describe("extractAnthropicUsage", () => {
|
|
|
24
24
|
};
|
|
25
25
|
const result = extractAnthropicUsage(usage);
|
|
26
26
|
expect(result).toEqual({
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
cachedTokens: 0,
|
|
30
|
-
totalTokens: 150,
|
|
27
|
+
promptTokens: 100,
|
|
28
|
+
completionTokens: 50,
|
|
31
29
|
});
|
|
32
30
|
});
|
|
33
31
|
it("should handle null cached token fields", () => {
|
|
@@ -39,10 +37,8 @@ describe("extractAnthropicUsage", () => {
|
|
|
39
37
|
};
|
|
40
38
|
const result = extractAnthropicUsage(usage);
|
|
41
39
|
expect(result).toEqual({
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
cachedTokens: 0,
|
|
45
|
-
totalTokens: 150,
|
|
40
|
+
promptTokens: 120,
|
|
41
|
+
completionTokens: 30,
|
|
46
42
|
});
|
|
47
43
|
});
|
|
48
44
|
});
|
|
@@ -50,19 +46,15 @@ describe("extractAnthropicUsage", () => {
|
|
|
50
46
|
it("should return zero values for null usage", () => {
|
|
51
47
|
const result = extractAnthropicUsage(null);
|
|
52
48
|
expect(result).toEqual({
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
cachedTokens: 0,
|
|
56
|
-
totalTokens: 0,
|
|
49
|
+
promptTokens: 0,
|
|
50
|
+
completionTokens: 0,
|
|
57
51
|
});
|
|
58
52
|
});
|
|
59
53
|
it("should return zero values for undefined usage", () => {
|
|
60
54
|
const result = extractAnthropicUsage(undefined);
|
|
61
55
|
expect(result).toEqual({
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
cachedTokens: 0,
|
|
65
|
-
totalTokens: 0,
|
|
56
|
+
promptTokens: 0,
|
|
57
|
+
completionTokens: 0,
|
|
66
58
|
});
|
|
67
59
|
});
|
|
68
60
|
it("should handle partial usage object", () => {
|
|
@@ -72,10 +64,8 @@ describe("extractAnthropicUsage", () => {
|
|
|
72
64
|
};
|
|
73
65
|
const result = extractAnthropicUsage(usage);
|
|
74
66
|
expect(result).toEqual({
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
cachedTokens: 0,
|
|
78
|
-
totalTokens: 80,
|
|
67
|
+
promptTokens: 80,
|
|
68
|
+
completionTokens: 0,
|
|
79
69
|
});
|
|
80
70
|
});
|
|
81
71
|
});
|
|
@@ -89,10 +79,8 @@ describe("extractAnthropicUsage", () => {
|
|
|
89
79
|
};
|
|
90
80
|
const result = extractAnthropicUsage(usage);
|
|
91
81
|
expect(result).toEqual({
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
cachedTokens: 0,
|
|
95
|
-
totalTokens: 0,
|
|
82
|
+
promptTokens: 0,
|
|
83
|
+
completionTokens: 0,
|
|
96
84
|
});
|
|
97
85
|
});
|
|
98
86
|
it("should handle very large token counts", () => {
|
|
@@ -104,10 +92,10 @@ describe("extractAnthropicUsage", () => {
|
|
|
104
92
|
};
|
|
105
93
|
const result = extractAnthropicUsage(usage);
|
|
106
94
|
expect(result).toEqual({
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
95
|
+
promptTokens: 1000000,
|
|
96
|
+
completionTokens: 500000,
|
|
97
|
+
cacheWriteTokens: 200000,
|
|
98
|
+
cacheReadTokens: 100000,
|
|
111
99
|
});
|
|
112
100
|
});
|
|
113
101
|
});
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AiEventStatus, FluxGate, FluxGateCostTrackingResponse, AiEventUsage } from "@fluxgate/sdk";
|
|
2
|
+
import { FluxGateContext } from "../types/types.js";
|
|
2
3
|
export declare function stopReasonToStatus(stopReason: string | null | undefined): AiEventStatus;
|
|
3
4
|
export declare function recordUsage(params: {
|
|
4
5
|
instance: FluxGate;
|
|
5
6
|
model: string;
|
|
6
7
|
latencyMs: number;
|
|
7
8
|
streaming: boolean;
|
|
8
|
-
context:
|
|
9
|
-
usage:
|
|
9
|
+
context: FluxGateContext | undefined;
|
|
10
|
+
usage: AiEventUsage;
|
|
10
11
|
status: AiEventStatus;
|
|
11
12
|
errorMessage?: string;
|
|
12
13
|
}): Promise<FluxGateCostTrackingResponse>;
|
|
@@ -1,13 +1,7 @@
|
|
|
1
|
-
function
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
normalized.user = user;
|
|
6
|
-
}
|
|
7
|
-
else if (user != null) {
|
|
8
|
-
normalized.user = user;
|
|
9
|
-
}
|
|
10
|
-
return normalized;
|
|
1
|
+
function toPerformanceStatus(status) {
|
|
2
|
+
return status === "ERROR" || status === "MALFORMED_REQUEST"
|
|
3
|
+
? "ERROR"
|
|
4
|
+
: "SUCCESS";
|
|
11
5
|
}
|
|
12
6
|
export function stopReasonToStatus(stopReason) {
|
|
13
7
|
if (!stopReason ||
|
|
@@ -28,29 +22,46 @@ export function stopReasonToStatus(stopReason) {
|
|
|
28
22
|
}
|
|
29
23
|
export async function recordUsage(params) {
|
|
30
24
|
const { context, latencyMs, model, streaming, instance, usage, status, errorMessage, } = params;
|
|
25
|
+
const resolvedServiceTier = context?.serviceTier;
|
|
26
|
+
const hasMetadata = resolvedServiceTier != null ||
|
|
27
|
+
context?.region != null ||
|
|
28
|
+
context?.openrouterCost != null ||
|
|
29
|
+
context?.cacheTtl != null ||
|
|
30
|
+
(context?.metadata != null && Object.keys(context.metadata).length > 0);
|
|
31
|
+
const metadata = hasMetadata
|
|
32
|
+
? {
|
|
33
|
+
serviceTier: resolvedServiceTier,
|
|
34
|
+
region: context?.region,
|
|
35
|
+
openrouterCost: context?.openrouterCost,
|
|
36
|
+
cacheTtl: context?.cacheTtl,
|
|
37
|
+
...context?.metadata,
|
|
38
|
+
}
|
|
39
|
+
: undefined;
|
|
31
40
|
const trackingData = await instance.recordEvent({
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
provider: "anthropic",
|
|
42
|
+
model,
|
|
43
|
+
user: context?.user,
|
|
44
|
+
feature: context?.feature,
|
|
45
|
+
step: context?.step,
|
|
46
|
+
sessionId: context?.sessionId,
|
|
47
|
+
conversationId: context?.conversationId,
|
|
48
|
+
timestamp: context?.timestamp,
|
|
49
|
+
performance: {
|
|
50
|
+
latency: latencyMs,
|
|
51
|
+
status: toPerformanceStatus(status),
|
|
42
52
|
isStreamed: streaming,
|
|
43
|
-
|
|
44
|
-
provider: "anthropic",
|
|
45
|
-
streamingDurationInMs: streaming ? latencyMs : undefined,
|
|
53
|
+
errorMessage: errorMessage ?? null,
|
|
46
54
|
},
|
|
55
|
+
usage,
|
|
56
|
+
...(metadata && { metadata }),
|
|
57
|
+
...(context?.costOverride && { costOverride: context.costOverride }),
|
|
47
58
|
});
|
|
48
59
|
return {
|
|
49
60
|
status,
|
|
50
61
|
errorMessage,
|
|
51
|
-
cost: trackingData?.
|
|
52
|
-
trackingId: trackingData?.
|
|
53
|
-
createdAt:
|
|
62
|
+
cost: trackingData?.totalCost ?? null,
|
|
63
|
+
trackingId: trackingData?.recordId ?? null,
|
|
64
|
+
createdAt: null,
|
|
54
65
|
};
|
|
55
66
|
}
|
|
56
67
|
export function extractResponseStatus(stopReason) {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { FluxGate, WithTracking } from "@fluxgate/sdk";
|
|
2
|
+
import { FluxGateContext } from "../types/types.js";
|
|
3
|
+
import type Anthropic from "@anthropic-ai/sdk";
|
|
4
|
+
import type { BetaMessage, BetaRawMessageStreamEvent } from "@anthropic-ai/sdk/resources/beta/messages/messages";
|
|
5
|
+
import { TrackedStream } from "./TrackedStream.js";
|
|
6
|
+
type OrigCreate = Anthropic["beta"]["messages"]["create"];
|
|
7
|
+
export declare function createBetaMessagesWrapper(original: OrigCreate, instance: FluxGate, context: FluxGateContext | undefined): (params: Parameters<OrigCreate>[0], options?: Parameters<OrigCreate>[1]) => Promise<WithTracking<BetaMessage> | TrackedStream<BetaRawMessageStreamEvent>>;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { extractAnthropicUsage } from "../utils/extractUsage.js";
|
|
2
|
+
import { isAsyncIterable } from "../utils/utils.js";
|
|
3
|
+
import { TrackedStream } from "./TrackedStream.js";
|
|
4
|
+
import { extractResponseStatus, recordUsage } from "../utils/recordUsage.js";
|
|
5
|
+
export function createBetaMessagesWrapper(original, instance, context) {
|
|
6
|
+
return async function wrappedBetaMessagesCreate(params, options) {
|
|
7
|
+
const start = performance.now();
|
|
8
|
+
let res;
|
|
9
|
+
try {
|
|
10
|
+
res = await original(params, options);
|
|
11
|
+
}
|
|
12
|
+
catch (err) {
|
|
13
|
+
await recordUsage({
|
|
14
|
+
instance,
|
|
15
|
+
model: params.model.toString(),
|
|
16
|
+
latencyMs: performance.now() - start,
|
|
17
|
+
streaming: !!params.stream,
|
|
18
|
+
context,
|
|
19
|
+
usage: extractAnthropicUsage(undefined),
|
|
20
|
+
status: "ERROR",
|
|
21
|
+
errorMessage: err.message,
|
|
22
|
+
});
|
|
23
|
+
throw err;
|
|
24
|
+
}
|
|
25
|
+
if (params.stream && isAsyncIterable(res)) {
|
|
26
|
+
let latestUsage;
|
|
27
|
+
let latestStopReason;
|
|
28
|
+
const trackingSource = (async function* () {
|
|
29
|
+
for await (const event of res) {
|
|
30
|
+
if (event.type === "message_delta") {
|
|
31
|
+
latestUsage = event.usage;
|
|
32
|
+
latestStopReason = event.delta.stop_reason;
|
|
33
|
+
}
|
|
34
|
+
yield event;
|
|
35
|
+
}
|
|
36
|
+
})();
|
|
37
|
+
return new TrackedStream(trackingSource, (_last, streamError) => {
|
|
38
|
+
const { status, errorMessage } = streamError
|
|
39
|
+
? {
|
|
40
|
+
status: "ERROR",
|
|
41
|
+
errorMessage: streamError.message,
|
|
42
|
+
}
|
|
43
|
+
: extractResponseStatus(latestStopReason);
|
|
44
|
+
return recordUsage({
|
|
45
|
+
instance,
|
|
46
|
+
model: params.model.toString(),
|
|
47
|
+
latencyMs: performance.now() - start,
|
|
48
|
+
streaming: true,
|
|
49
|
+
context,
|
|
50
|
+
usage: extractAnthropicUsage(latestUsage),
|
|
51
|
+
status,
|
|
52
|
+
errorMessage,
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
const message = res;
|
|
57
|
+
const { status, errorMessage } = extractResponseStatus(message.stop_reason);
|
|
58
|
+
const fluxGateCostTrackingResponse = await recordUsage({
|
|
59
|
+
instance,
|
|
60
|
+
model: params.model.toString(),
|
|
61
|
+
latencyMs: performance.now() - start,
|
|
62
|
+
streaming: false,
|
|
63
|
+
context,
|
|
64
|
+
usage: extractAnthropicUsage(message.usage),
|
|
65
|
+
status,
|
|
66
|
+
errorMessage,
|
|
67
|
+
});
|
|
68
|
+
return Object.assign(message, { fluxGateCostTrackingResponse });
|
|
69
|
+
};
|
|
70
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { FluxGate, WithTracking } from "@fluxgate/sdk";
|
|
2
|
+
import { FluxGateContext } from "../types/types.js";
|
|
3
|
+
import type Anthropic from "@anthropic-ai/sdk";
|
|
4
|
+
import type { Completion } from "@anthropic-ai/sdk/resources/completions";
|
|
5
|
+
import { TrackedStream } from "./TrackedStream.js";
|
|
6
|
+
type OrigCreate = Anthropic["completions"]["create"];
|
|
7
|
+
export declare function createCompletionsWrapper(original: OrigCreate, instance: FluxGate, context: FluxGateContext | undefined): (params: Parameters<OrigCreate>[0], options?: Parameters<OrigCreate>[1]) => Promise<WithTracking<Completion> | TrackedStream<Completion>>;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { extractAnthropicUsage } from "../utils/extractUsage.js";
|
|
2
|
+
import { isAsyncIterable } from "../utils/utils.js";
|
|
3
|
+
import { TrackedStream } from "./TrackedStream.js";
|
|
4
|
+
import { extractResponseStatus, recordUsage } from "../utils/recordUsage.js";
|
|
5
|
+
export function createCompletionsWrapper(original, instance, context) {
|
|
6
|
+
return async function wrappedCompletionsCreate(params, options) {
|
|
7
|
+
const start = performance.now();
|
|
8
|
+
let res;
|
|
9
|
+
try {
|
|
10
|
+
res = await original(params, options);
|
|
11
|
+
}
|
|
12
|
+
catch (err) {
|
|
13
|
+
await recordUsage({
|
|
14
|
+
instance,
|
|
15
|
+
model: params.model.toString(),
|
|
16
|
+
latencyMs: performance.now() - start,
|
|
17
|
+
streaming: !!params.stream,
|
|
18
|
+
context,
|
|
19
|
+
usage: extractAnthropicUsage(undefined),
|
|
20
|
+
status: "ERROR",
|
|
21
|
+
errorMessage: err.message,
|
|
22
|
+
});
|
|
23
|
+
throw err;
|
|
24
|
+
}
|
|
25
|
+
if (params.stream && isAsyncIterable(res)) {
|
|
26
|
+
let latestStopReason;
|
|
27
|
+
const trackingSource = (async function* () {
|
|
28
|
+
for await (const chunk of res) {
|
|
29
|
+
if (chunk.stop_reason) {
|
|
30
|
+
latestStopReason = chunk.stop_reason;
|
|
31
|
+
}
|
|
32
|
+
yield chunk;
|
|
33
|
+
}
|
|
34
|
+
})();
|
|
35
|
+
return new TrackedStream(trackingSource, (_last, streamError) => {
|
|
36
|
+
const { status, errorMessage } = streamError
|
|
37
|
+
? {
|
|
38
|
+
status: "ERROR",
|
|
39
|
+
errorMessage: streamError.message,
|
|
40
|
+
}
|
|
41
|
+
: extractResponseStatus(latestStopReason);
|
|
42
|
+
// The legacy completions API does not return token usage
|
|
43
|
+
return recordUsage({
|
|
44
|
+
instance,
|
|
45
|
+
model: params.model.toString(),
|
|
46
|
+
latencyMs: performance.now() - start,
|
|
47
|
+
streaming: true,
|
|
48
|
+
context,
|
|
49
|
+
usage: extractAnthropicUsage(undefined),
|
|
50
|
+
status,
|
|
51
|
+
errorMessage,
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
const completion = res;
|
|
56
|
+
const { status, errorMessage } = extractResponseStatus(completion.stop_reason);
|
|
57
|
+
// The legacy completions API does not return token usage
|
|
58
|
+
const fluxGateCostTrackingResponse = await recordUsage({
|
|
59
|
+
instance,
|
|
60
|
+
model: params.model.toString(),
|
|
61
|
+
latencyMs: performance.now() - start,
|
|
62
|
+
streaming: false,
|
|
63
|
+
context,
|
|
64
|
+
usage: extractAnthropicUsage(undefined),
|
|
65
|
+
status,
|
|
66
|
+
errorMessage,
|
|
67
|
+
});
|
|
68
|
+
return Object.assign(completion, { fluxGateCostTrackingResponse });
|
|
69
|
+
};
|
|
70
|
+
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FluxGate, WithTracking } from "@fluxgate/sdk";
|
|
2
|
+
import { FluxGateContext } from "../types/types.js";
|
|
2
3
|
import type Anthropic from "@anthropic-ai/sdk";
|
|
3
4
|
import type { Message, RawMessageStreamEvent } from "@anthropic-ai/sdk/resources/messages";
|
|
4
5
|
import { TrackedStream } from "./TrackedStream.js";
|
|
5
6
|
import { TrackedAnthropic } from "../types/types.js";
|
|
6
7
|
type OrigCreate = Anthropic["messages"]["create"];
|
|
7
|
-
export declare function withAnthropicTracking(client: Anthropic, instance: FluxGate, context?:
|
|
8
|
-
export declare function createMessagesWrapper(original: OrigCreate, instance: FluxGate, context:
|
|
8
|
+
export declare function withAnthropicTracking(client: Anthropic, instance: FluxGate, context?: FluxGateContext): TrackedAnthropic;
|
|
9
|
+
export declare function createMessagesWrapper(original: OrigCreate, instance: FluxGate, context: FluxGateContext | undefined): (params: Parameters<OrigCreate>[0], options?: Parameters<OrigCreate>[1]) => Promise<WithTracking<Message> | TrackedStream<RawMessageStreamEvent>>;
|
|
9
10
|
export {};
|
|
@@ -2,10 +2,17 @@ import { extractAnthropicUsage } from "../utils/extractUsage.js";
|
|
|
2
2
|
import { isAsyncIterable } from "../utils/utils.js";
|
|
3
3
|
import { TrackedStream } from "./TrackedStream.js";
|
|
4
4
|
import { extractResponseStatus, recordUsage } from "../utils/recordUsage.js";
|
|
5
|
+
import { createCompletionsWrapper } from "./completions.js";
|
|
6
|
+
import { createBetaMessagesWrapper } from "./betaMessages.js";
|
|
5
7
|
export function withAnthropicTracking(client, instance, context) {
|
|
6
8
|
const wrappedClient = Object.create(Object.getPrototypeOf(client), Object.getOwnPropertyDescriptors(client));
|
|
7
9
|
wrappedClient.messages = Object.create(Object.getPrototypeOf(client.messages), Object.getOwnPropertyDescriptors(client.messages));
|
|
8
10
|
wrappedClient.messages.create = createMessagesWrapper(client.messages.create.bind(client.messages), instance, context);
|
|
11
|
+
wrappedClient.completions = Object.create(Object.getPrototypeOf(client.completions), Object.getOwnPropertyDescriptors(client.completions));
|
|
12
|
+
wrappedClient.completions.create = createCompletionsWrapper(client.completions.create.bind(client.completions), instance, context);
|
|
13
|
+
wrappedClient.beta = Object.create(Object.getPrototypeOf(client.beta), Object.getOwnPropertyDescriptors(client.beta));
|
|
14
|
+
wrappedClient.beta.messages = Object.create(Object.getPrototypeOf(client.beta.messages), Object.getOwnPropertyDescriptors(client.beta.messages));
|
|
15
|
+
wrappedClient.beta.messages.create = createBetaMessagesWrapper(client.beta.messages.create.bind(client.beta.messages), instance, context);
|
|
9
16
|
return wrappedClient;
|
|
10
17
|
}
|
|
11
18
|
export function createMessagesWrapper(original, instance, context) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluxgate/anthropic",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"description": "Anthropic wrapper for FluxGate token usage, latency, and cost monitoring.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"access": "public"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@fluxgate/sdk": "^0.0.
|
|
53
|
+
"@fluxgate/sdk": "^0.0.3-dev.0"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
56
|
"@anthropic-ai/sdk": "^0.50.0"
|