@amitdeshmukh/ax-crew 3.7.1 → 3.8.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/CHANGELOG.md CHANGED
@@ -5,6 +5,16 @@ This Changelog format is based on [Keep a Changelog]
5
5
  adheres to [Semantic Versioning](https://semver.org/spec/
6
6
  v2.0.0.html).
7
7
 
8
+ ## [3.8.1] - 2024-03-28
9
+
10
+ ### Added
11
+ - Enhanced type definitions for model usage metrics with nested token structure support
12
+
13
+ ### Changed
14
+ - Improved error handling and robustness in cost calculations
15
+ - Better null handling in usage cost tracking
16
+ - Updated type definitions in `src/types.ts` to support both direct and nested token structures
17
+
8
18
  ## [3.7.1] - 2025-03-27
9
19
 
10
20
  ### Fixed
@@ -5,8 +5,8 @@ import type { StateInstance, ModelUsage, ModelInfo, UsageCost, AggregatedCosts }
5
5
  */
6
6
  export declare class StateFulAxAgentUsage {
7
7
  static STATE_KEY_PREFIX: string;
8
- static calculateCost(modelUsage: ModelUsage, modelInfo: ModelInfo): UsageCost;
9
- static trackCostInState(agentName: string, cost: UsageCost, state: StateInstance): void;
8
+ static calculateCost(modelUsage: ModelUsage, modelInfo: ModelInfo): UsageCost | null;
9
+ static trackCostInState(agentName: string, cost: UsageCost | null, state: StateInstance): void;
10
10
  static getAggregatedCosts(state: StateInstance): AggregatedCosts;
11
11
  static resetCosts(state: StateInstance): void;
12
12
  }
@@ -5,45 +5,69 @@ import { Decimal } from 'decimal.js';
5
5
  */
6
6
  export class StateFulAxAgentUsage {
7
7
  static calculateCost(modelUsage, modelInfo) {
8
- const { promptTokens, completionTokens } = modelUsage;
8
+ // Handle both direct properties and nested tokens structure
9
+ const promptTokens = modelUsage.tokens?.promptTokens ?? modelUsage.promptTokens;
10
+ const completionTokens = modelUsage.tokens?.completionTokens ?? modelUsage.completionTokens;
9
11
  const { promptTokenCostPer1M, completionTokenCostPer1M } = modelInfo;
10
- // Use Decimal for precise calculations
11
- const promptCost = new Decimal(promptTokens)
12
- .div(1000000)
13
- .mul(promptTokenCostPer1M)
14
- .toDP(10); // Keep 10 decimal places
15
- const completionCost = new Decimal(completionTokens)
16
- .div(1000000)
17
- .mul(completionTokenCostPer1M)
18
- .toDP(10);
19
- const totalCost = promptCost.plus(completionCost).toDP(10);
20
- return {
21
- promptCost: promptCost.toString(),
22
- completionCost: completionCost.toString(),
23
- totalCost: totalCost.toString(),
24
- tokenMetrics: {
25
- promptTokens,
26
- completionTokens,
27
- totalTokens: promptTokens + completionTokens
28
- }
29
- };
12
+ // Return null instead of throwing errors for invalid values
13
+ if (typeof promptTokens !== 'number' || isNaN(promptTokens) ||
14
+ typeof completionTokens !== 'number' || isNaN(completionTokens) ||
15
+ typeof promptTokenCostPer1M !== 'number' || isNaN(promptTokenCostPer1M) ||
16
+ typeof completionTokenCostPer1M !== 'number' || isNaN(completionTokenCostPer1M)) {
17
+ return null;
18
+ }
19
+ try {
20
+ // Use Decimal for precise calculations
21
+ const promptCost = new Decimal(promptTokens)
22
+ .div(1000000)
23
+ .mul(promptTokenCostPer1M)
24
+ .toDP(10); // Keep 10 decimal places
25
+ const completionCost = new Decimal(completionTokens)
26
+ .div(1000000)
27
+ .mul(completionTokenCostPer1M)
28
+ .toDP(10);
29
+ const totalCost = promptCost.plus(completionCost).toDP(10);
30
+ return {
31
+ promptCost: promptCost.toString(),
32
+ completionCost: completionCost.toString(),
33
+ totalCost: totalCost.toString(),
34
+ tokenMetrics: {
35
+ promptTokens,
36
+ completionTokens,
37
+ totalTokens: promptTokens + completionTokens
38
+ }
39
+ };
40
+ }
41
+ catch (error) {
42
+ // If any decimal calculation fails, return null instead of throwing
43
+ return null;
44
+ }
30
45
  }
31
46
  static trackCostInState(agentName, cost, state) {
47
+ // If cost is null, skip tracking
48
+ if (!cost)
49
+ return;
32
50
  const stateKey = `${this.STATE_KEY_PREFIX}${agentName}`;
33
51
  const existingCost = state.get(stateKey);
34
52
  if (existingCost) {
35
- // Aggregate with existing cost using Decimal
36
- const aggregatedCost = {
37
- promptCost: new Decimal(existingCost.promptCost).plus(cost.promptCost).toDP(10).toString(),
38
- completionCost: new Decimal(existingCost.completionCost).plus(cost.completionCost).toDP(10).toString(),
39
- totalCost: new Decimal(existingCost.totalCost).plus(cost.totalCost).toDP(10).toString(),
40
- tokenMetrics: {
41
- promptTokens: existingCost.tokenMetrics.promptTokens + cost.tokenMetrics.promptTokens,
42
- completionTokens: existingCost.tokenMetrics.completionTokens + cost.tokenMetrics.completionTokens,
43
- totalTokens: existingCost.tokenMetrics.totalTokens + cost.tokenMetrics.totalTokens
44
- }
45
- };
46
- state.set(stateKey, aggregatedCost);
53
+ try {
54
+ // Aggregate with existing cost using Decimal
55
+ const aggregatedCost = {
56
+ promptCost: new Decimal(existingCost.promptCost).plus(cost.promptCost).toDP(10).toString(),
57
+ completionCost: new Decimal(existingCost.completionCost).plus(cost.completionCost).toDP(10).toString(),
58
+ totalCost: new Decimal(existingCost.totalCost).plus(cost.totalCost).toDP(10).toString(),
59
+ tokenMetrics: {
60
+ promptTokens: existingCost.tokenMetrics.promptTokens + cost.tokenMetrics.promptTokens,
61
+ completionTokens: existingCost.tokenMetrics.completionTokens + cost.tokenMetrics.completionTokens,
62
+ totalTokens: existingCost.tokenMetrics.totalTokens + cost.tokenMetrics.totalTokens
63
+ }
64
+ };
65
+ state.set(stateKey, aggregatedCost);
66
+ }
67
+ catch (error) {
68
+ // If aggregation fails, just use the new cost
69
+ state.set(stateKey, cost);
70
+ }
47
71
  }
48
72
  else {
49
73
  // First time tracking this agent's cost
@@ -68,8 +68,15 @@ class StatefulAxAgent extends AxAgent {
68
68
  // Get the usage cost for the most recent run of the agent
69
69
  getLastUsageCost() {
70
70
  const { modelUsage, modelInfo, defaults } = this.axai;
71
- const currentModelInfo = modelInfo?.find((m) => m.name === defaults.model);
72
- if (!currentModelInfo || !modelUsage) {
71
+ // Check if all required properties exist
72
+ if (!modelUsage?.promptTokens || !modelUsage?.completionTokens) {
73
+ return null;
74
+ }
75
+ if (!modelInfo || !defaults?.model) {
76
+ return null;
77
+ }
78
+ const currentModelInfo = modelInfo.find((m) => m.name === defaults.model);
79
+ if (!currentModelInfo?.promptTokenCostPer1M || !currentModelInfo?.completionTokenCostPer1M) {
73
80
  return null;
74
81
  }
75
82
  return StateFulAxAgentUsage.calculateCost(modelUsage, currentModelInfo);
package/dist/types.d.ts CHANGED
@@ -32,13 +32,22 @@ type FunctionRegistryType = {
32
32
  };
33
33
  /**
34
34
  * The usage metrics of the model.
35
- * promptTokens: number;
36
- * completionTokens: number;
35
+ * Supports both direct token properties and nested tokens structure
37
36
  */
38
- interface ModelUsage {
39
- promptTokens: number;
40
- completionTokens: number;
37
+ interface ModelUsageBase {
38
+ promptTokens?: number;
39
+ completionTokens?: number;
40
+ }
41
+ interface ModelUsageNested {
42
+ ai?: string;
43
+ model?: string;
44
+ tokens?: {
45
+ totalTokens?: number;
46
+ promptTokens: number;
47
+ completionTokens: number;
48
+ };
41
49
  }
50
+ type ModelUsage = ModelUsageBase & ModelUsageNested;
42
51
  /**
43
52
  * The published cost for using the model.
44
53
  * promptTokenCostPer1M: number;
@@ -11,10 +11,10 @@ const config = {
11
11
  description: "Completes a user specified task",
12
12
  signature:
13
13
  'question:string "a question to be answered" -> answer:string "the answer to the question"',
14
- provider: "openai",
15
- providerKeyName: "OPENAI_API_KEY",
14
+ provider: "google-gemini",
15
+ providerKeyName: "GEMINI_API_KEY",
16
16
  ai: {
17
- model: "gpt-4o-mini",
17
+ model: "gemini-2.0-flash",
18
18
  maxTokens: 1000,
19
19
  temperature: 0,
20
20
  },
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@amitdeshmukh/ax-crew",
4
- "version": "3.7.1",
4
+ "version": "3.8.1",
5
5
  "description": "Build and launch a crew of AI agents with shared state. Built with axllm.dev",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -15,52 +15,75 @@ import type {
15
15
  export class StateFulAxAgentUsage {
16
16
  static STATE_KEY_PREFIX = 'agent_usage_';
17
17
 
18
- static calculateCost(modelUsage: ModelUsage, modelInfo: ModelInfo): UsageCost {
19
- const { promptTokens, completionTokens } = modelUsage;
18
+ static calculateCost(modelUsage: ModelUsage, modelInfo: ModelInfo): UsageCost | null {
19
+ // Handle both direct properties and nested tokens structure
20
+ const promptTokens = (modelUsage as any).tokens?.promptTokens ?? modelUsage.promptTokens;
21
+ const completionTokens = (modelUsage as any).tokens?.completionTokens ?? modelUsage.completionTokens;
20
22
  const { promptTokenCostPer1M, completionTokenCostPer1M } = modelInfo;
21
23
 
22
- // Use Decimal for precise calculations
23
- const promptCost = new Decimal(promptTokens)
24
- .div(1000000)
25
- .mul(promptTokenCostPer1M)
26
- .toDP(10); // Keep 10 decimal places
24
+ // Return null instead of throwing errors for invalid values
25
+ if (typeof promptTokens !== 'number' || isNaN(promptTokens) ||
26
+ typeof completionTokens !== 'number' || isNaN(completionTokens) ||
27
+ typeof promptTokenCostPer1M !== 'number' || isNaN(promptTokenCostPer1M) ||
28
+ typeof completionTokenCostPer1M !== 'number' || isNaN(completionTokenCostPer1M)) {
29
+ return null;
30
+ }
27
31
 
28
- const completionCost = new Decimal(completionTokens)
29
- .div(1000000)
30
- .mul(completionTokenCostPer1M)
31
- .toDP(10);
32
+ try {
33
+ // Use Decimal for precise calculations
34
+ const promptCost = new Decimal(promptTokens)
35
+ .div(1000000)
36
+ .mul(promptTokenCostPer1M)
37
+ .toDP(10); // Keep 10 decimal places
32
38
 
33
- const totalCost = promptCost.plus(completionCost).toDP(10);
39
+ const completionCost = new Decimal(completionTokens)
40
+ .div(1000000)
41
+ .mul(completionTokenCostPer1M)
42
+ .toDP(10);
34
43
 
35
- return {
36
- promptCost: promptCost.toString(),
37
- completionCost: completionCost.toString(),
38
- totalCost: totalCost.toString(),
39
- tokenMetrics: {
40
- promptTokens,
41
- completionTokens,
42
- totalTokens: promptTokens + completionTokens
43
- }
44
- };
44
+ const totalCost = promptCost.plus(completionCost).toDP(10);
45
+
46
+ return {
47
+ promptCost: promptCost.toString(),
48
+ completionCost: completionCost.toString(),
49
+ totalCost: totalCost.toString(),
50
+ tokenMetrics: {
51
+ promptTokens,
52
+ completionTokens,
53
+ totalTokens: promptTokens + completionTokens
54
+ }
55
+ };
56
+ } catch (error) {
57
+ // If any decimal calculation fails, return null instead of throwing
58
+ return null;
59
+ }
45
60
  }
46
61
 
47
- static trackCostInState(agentName: string, cost: UsageCost, state: StateInstance) {
62
+ static trackCostInState(agentName: string, cost: UsageCost | null, state: StateInstance) {
63
+ // If cost is null, skip tracking
64
+ if (!cost) return;
65
+
48
66
  const stateKey = `${this.STATE_KEY_PREFIX}${agentName}`;
49
67
  const existingCost = state.get(stateKey) as UsageCost | undefined;
50
68
 
51
69
  if (existingCost) {
52
- // Aggregate with existing cost using Decimal
53
- const aggregatedCost: UsageCost = {
54
- promptCost: new Decimal(existingCost.promptCost).plus(cost.promptCost).toDP(10).toString(),
55
- completionCost: new Decimal(existingCost.completionCost).plus(cost.completionCost).toDP(10).toString(),
56
- totalCost: new Decimal(existingCost.totalCost).plus(cost.totalCost).toDP(10).toString(),
57
- tokenMetrics: {
58
- promptTokens: existingCost.tokenMetrics.promptTokens + cost.tokenMetrics.promptTokens,
59
- completionTokens: existingCost.tokenMetrics.completionTokens + cost.tokenMetrics.completionTokens,
60
- totalTokens: existingCost.tokenMetrics.totalTokens + cost.tokenMetrics.totalTokens
61
- }
62
- };
63
- state.set(stateKey, aggregatedCost);
70
+ try {
71
+ // Aggregate with existing cost using Decimal
72
+ const aggregatedCost: UsageCost = {
73
+ promptCost: new Decimal(existingCost.promptCost).plus(cost.promptCost).toDP(10).toString(),
74
+ completionCost: new Decimal(existingCost.completionCost).plus(cost.completionCost).toDP(10).toString(),
75
+ totalCost: new Decimal(existingCost.totalCost).plus(cost.totalCost).toDP(10).toString(),
76
+ tokenMetrics: {
77
+ promptTokens: existingCost.tokenMetrics.promptTokens + cost.tokenMetrics.promptTokens,
78
+ completionTokens: existingCost.tokenMetrics.completionTokens + cost.tokenMetrics.completionTokens,
79
+ totalTokens: existingCost.tokenMetrics.totalTokens + cost.tokenMetrics.totalTokens
80
+ }
81
+ };
82
+ state.set(stateKey, aggregatedCost);
83
+ } catch (error) {
84
+ // If aggregation fails, just use the new cost
85
+ state.set(stateKey, cost);
86
+ }
64
87
  } else {
65
88
  // First time tracking this agent's cost
66
89
  state.set(stateKey, cost);
@@ -144,9 +144,19 @@ class StatefulAxAgent extends AxAgent<any, any> {
144
144
  // Get the usage cost for the most recent run of the agent
145
145
  getLastUsageCost(): UsageCost | null {
146
146
  const { modelUsage, modelInfo, defaults } = this.axai;
147
- const currentModelInfo = modelInfo?.find((m: { name: string }) => m.name === defaults.model);
148
147
 
149
- if (!currentModelInfo || !modelUsage) {
148
+ // Check if all required properties exist
149
+ if (!modelUsage?.promptTokens || !modelUsage?.completionTokens) {
150
+ return null;
151
+ }
152
+
153
+ if (!modelInfo || !defaults?.model) {
154
+ return null;
155
+ }
156
+
157
+ const currentModelInfo = modelInfo.find((m: { name: string }) => m.name === defaults.model);
158
+
159
+ if (!currentModelInfo?.promptTokenCostPer1M || !currentModelInfo?.completionTokenCostPer1M) {
150
160
  return null;
151
161
  }
152
162
 
package/src/types.ts CHANGED
@@ -36,14 +36,25 @@ type FunctionRegistryType = {
36
36
 
37
37
  /**
38
38
  * The usage metrics of the model.
39
- * promptTokens: number;
40
- * completionTokens: number;
39
+ * Supports both direct token properties and nested tokens structure
41
40
  */
42
- interface ModelUsage {
43
- promptTokens: number;
44
- completionTokens: number;
41
+ interface ModelUsageBase {
42
+ promptTokens?: number;
43
+ completionTokens?: number;
45
44
  }
46
45
 
46
+ interface ModelUsageNested {
47
+ ai?: string;
48
+ model?: string;
49
+ tokens?: {
50
+ totalTokens?: number;
51
+ promptTokens: number;
52
+ completionTokens: number;
53
+ };
54
+ }
55
+
56
+ type ModelUsage = ModelUsageBase & ModelUsageNested;
57
+
47
58
  /**
48
59
  * The published cost for using the model.
49
60
  * promptTokenCostPer1M: number;