@amitdeshmukh/ax-crew 3.5.1 → 3.6.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 +14 -0
- package/README.md +21 -3
- package/dist/agents/agentUseCosts.d.ts +13 -11
- package/dist/agents/index.d.ts +11 -3
- package/dist/agents/index.js +27 -15
- package/examples/README.md +6 -3
- package/examples/basic-researcher-writer.ts +18 -19
- package/examples/solve-math-problem.ts +10 -11
- package/examples/write-post-and-publish-to-wordpress.ts +61 -66
- package/index.d.ts +4 -10
- package/package.json +2 -2
- package/src/agents/agentUseCosts.ts +15 -11
- package/src/agents/index.ts +30 -16
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,20 @@ 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.5.3] - 2025-02-20
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- New methods for crew level cost tracking:
|
|
12
|
+
- `getAggregatedCosts()` for crew-wide metrics
|
|
13
|
+
- `resetCosts()` for resetting cost tracking
|
|
14
|
+
- Enhanced `forward()` method with automatic cost tracking
|
|
15
|
+
- New TypeScript interfaces for better type safety in cost tracking
|
|
16
|
+
- Improved token metrics aggregation with detailed per-agent breakdowns
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
- Improved handling of cost calculations for sub-agent calls
|
|
20
|
+
- Updated [examples](./examples/) to demonstrate the new cost tracking methods
|
|
21
|
+
|
|
8
22
|
## [3.5.2] - 2025-02-03
|
|
9
23
|
|
|
10
24
|
### Added
|
package/README.md
CHANGED
|
@@ -16,10 +16,12 @@ Install this package:
|
|
|
16
16
|
```bash
|
|
17
17
|
npm install @amitdeshmukh/ax-crew
|
|
18
18
|
```
|
|
19
|
-
AxLLM is a peer dependency, so you will need to install it separately.
|
|
19
|
+
AxLLM is a peer dependency, so you will need to install it separately.
|
|
20
|
+
|
|
21
|
+
**Note:** Currently, we support AxLLM v10.0.9. We are working on updating this package to support the latest version of AxLLM as it introduces some breaking changes.
|
|
20
22
|
|
|
21
23
|
```bash
|
|
22
|
-
npm install @ax-llm/ax@
|
|
24
|
+
npm install @ax-llm/ax@10.0.9
|
|
23
25
|
```
|
|
24
26
|
|
|
25
27
|
### TypeScript Support
|
|
@@ -323,7 +325,7 @@ The package provides precise cost tracking capabilities for monitoring API usage
|
|
|
323
325
|
const response = await Planner.forward({ task: userQuery });
|
|
324
326
|
|
|
325
327
|
// Get individual agent costs
|
|
326
|
-
const agentCost = Planner.
|
|
328
|
+
const agentCost = Planner.getLastUsageCost();
|
|
327
329
|
console.log(agentCost);
|
|
328
330
|
/* Output example:
|
|
329
331
|
{
|
|
@@ -338,6 +340,22 @@ console.log(agentCost);
|
|
|
338
340
|
}
|
|
339
341
|
*/
|
|
340
342
|
|
|
343
|
+
// Get cumulative costs for the agent
|
|
344
|
+
const cumulativeCost = Planner.getAccumulatedCosts();
|
|
345
|
+
console.log(cumulativeCost);
|
|
346
|
+
/* Output example:
|
|
347
|
+
{
|
|
348
|
+
promptCost: "0.0003637500000",
|
|
349
|
+
completionCost: "0.0006100000000",
|
|
350
|
+
totalCost: "0.0009737500000",
|
|
351
|
+
tokenMetrics: {
|
|
352
|
+
promptTokens: 291,
|
|
353
|
+
completionTokens: 122,
|
|
354
|
+
totalTokens: 413
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
*/
|
|
358
|
+
|
|
341
359
|
// Get aggregated costs for all agents in the crew
|
|
342
360
|
const crewCosts = crew.getAggregatedCosts();
|
|
343
361
|
console.log(crewCosts);
|
|
@@ -17,20 +17,22 @@ export interface UsageCost {
|
|
|
17
17
|
totalTokens: number;
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
|
+
export interface AggregatedMetrics {
|
|
21
|
+
promptTokens: number;
|
|
22
|
+
completionTokens: number;
|
|
23
|
+
totalTokens: number;
|
|
24
|
+
promptCost: string;
|
|
25
|
+
completionCost: string;
|
|
26
|
+
}
|
|
27
|
+
export interface AggregatedCosts {
|
|
28
|
+
totalCost: string;
|
|
29
|
+
byAgent: Record<string, UsageCost>;
|
|
30
|
+
aggregatedMetrics: AggregatedMetrics;
|
|
31
|
+
}
|
|
20
32
|
export declare class StateFulAxAgentUsage {
|
|
21
33
|
static STATE_KEY_PREFIX: string;
|
|
22
34
|
static calculateCost(modelUsage: ModelUsage, modelInfo: ModelInfo): UsageCost;
|
|
23
35
|
static trackCostInState(agentName: string, cost: UsageCost, state: StateInstance): void;
|
|
24
|
-
static getAggregatedCosts(state: StateInstance):
|
|
25
|
-
totalCost: string;
|
|
26
|
-
byAgent: Record<string, UsageCost>;
|
|
27
|
-
aggregatedMetrics: {
|
|
28
|
-
promptTokens: number;
|
|
29
|
-
completionTokens: number;
|
|
30
|
-
totalTokens: number;
|
|
31
|
-
promptCost: string;
|
|
32
|
-
completionCost: string;
|
|
33
|
-
};
|
|
34
|
-
};
|
|
36
|
+
static getAggregatedCosts(state: StateInstance): AggregatedCosts;
|
|
35
37
|
static resetCosts(state: StateInstance): void;
|
|
36
38
|
}
|
package/dist/agents/index.d.ts
CHANGED
|
@@ -18,9 +18,8 @@ declare class StatefulAxAgent extends AxAgent<any, any> {
|
|
|
18
18
|
}>, state: StateInstance);
|
|
19
19
|
forward(values: Record<string, any>, options?: Readonly<AxProgramForwardOptions>): Promise<Record<string, any>>;
|
|
20
20
|
forward(ai: AxAI, values: Record<string, any>, options?: Readonly<AxProgramForwardOptions>): Promise<Record<string, any>>;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
resetCosts(): void;
|
|
21
|
+
getLastUsageCost(): UsageCost | null;
|
|
22
|
+
getAccumulatedCosts(): UsageCost | null;
|
|
24
23
|
}
|
|
25
24
|
/**
|
|
26
25
|
* Represents a crew of agents with shared state functionality.
|
|
@@ -62,5 +61,14 @@ declare class AxCrew {
|
|
|
62
61
|
* Cleans up the crew by dereferencing agents and resetting the state.
|
|
63
62
|
*/
|
|
64
63
|
destroy(): void;
|
|
64
|
+
/**
|
|
65
|
+
* Gets aggregated costs for all agents in the crew
|
|
66
|
+
* @returns Aggregated cost information for all agents
|
|
67
|
+
*/
|
|
68
|
+
getAggregatedCosts(): ReturnType<typeof StateFulAxAgentUsage.getAggregatedCosts>;
|
|
69
|
+
/**
|
|
70
|
+
* Resets all cost tracking for the crew
|
|
71
|
+
*/
|
|
72
|
+
resetCosts(): void;
|
|
65
73
|
}
|
|
66
74
|
export { AxCrew };
|
package/dist/agents/index.js
CHANGED
|
@@ -17,43 +17,42 @@ class StatefulAxAgent extends AxAgent {
|
|
|
17
17
|
this.agentName = options.name;
|
|
18
18
|
// Set examples if provided
|
|
19
19
|
if (examples && examples.length > 0) {
|
|
20
|
-
|
|
20
|
+
super.setExamples(examples);
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
// Implementation
|
|
24
24
|
async forward(first, second, third) {
|
|
25
|
-
// Sub-agent case (called with AI service)
|
|
26
25
|
let result;
|
|
26
|
+
// Track costs regardless of whether it's a direct or sub-agent call
|
|
27
|
+
// This ensures we capture multiple legitimate calls to the same agent
|
|
27
28
|
if ('apiURL' in first) {
|
|
29
|
+
// Sub-agent case (called with AI service)
|
|
28
30
|
result = await super.forward(this.axai, second, third);
|
|
29
31
|
}
|
|
30
32
|
else {
|
|
31
33
|
// Direct call case
|
|
32
34
|
result = await super.forward(this.axai, first, second);
|
|
33
35
|
}
|
|
34
|
-
// Track costs
|
|
35
|
-
const cost = this.
|
|
36
|
+
// Track costs after the call
|
|
37
|
+
const cost = this.getLastUsageCost();
|
|
36
38
|
if (cost) {
|
|
37
39
|
StateFulAxAgentUsage.trackCostInState(this.agentName, cost, this.state);
|
|
38
40
|
}
|
|
39
41
|
return result;
|
|
40
42
|
}
|
|
41
|
-
// Get the usage cost for
|
|
42
|
-
|
|
43
|
-
const { modelUsage, modelInfo,
|
|
44
|
-
const currentModelInfo = modelInfo?.find((m) => m.name ===
|
|
43
|
+
// Get the usage cost for the most recent run of the agent
|
|
44
|
+
getLastUsageCost() {
|
|
45
|
+
const { modelUsage, modelInfo, defaults } = this.axai;
|
|
46
|
+
const currentModelInfo = modelInfo?.find((m) => m.name === defaults.model);
|
|
45
47
|
if (!currentModelInfo || !modelUsage) {
|
|
46
48
|
return null;
|
|
47
49
|
}
|
|
48
50
|
return StateFulAxAgentUsage.calculateCost(modelUsage, currentModelInfo);
|
|
49
51
|
}
|
|
50
|
-
// Get
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
// Reset all cost tracking
|
|
55
|
-
resetCosts() {
|
|
56
|
-
StateFulAxAgentUsage.resetCosts(this.state);
|
|
52
|
+
// Get the accumulated costs for all runs of this agent
|
|
53
|
+
getAccumulatedCosts() {
|
|
54
|
+
const stateKey = `${StateFulAxAgentUsage.STATE_KEY_PREFIX}${this.agentName}`;
|
|
55
|
+
return this.state.get(stateKey);
|
|
57
56
|
}
|
|
58
57
|
}
|
|
59
58
|
/**
|
|
@@ -147,5 +146,18 @@ class AxCrew {
|
|
|
147
146
|
this.agents = null;
|
|
148
147
|
this.state.reset();
|
|
149
148
|
}
|
|
149
|
+
/**
|
|
150
|
+
* Gets aggregated costs for all agents in the crew
|
|
151
|
+
* @returns Aggregated cost information for all agents
|
|
152
|
+
*/
|
|
153
|
+
getAggregatedCosts() {
|
|
154
|
+
return StateFulAxAgentUsage.getAggregatedCosts(this.state);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Resets all cost tracking for the crew
|
|
158
|
+
*/
|
|
159
|
+
resetCosts() {
|
|
160
|
+
StateFulAxAgentUsage.resetCosts(this.state);
|
|
161
|
+
}
|
|
150
162
|
}
|
|
151
163
|
export { AxCrew };
|
package/examples/README.md
CHANGED
|
@@ -135,13 +135,16 @@ A basic example showing core AxCrew features:
|
|
|
135
135
|
All examples include cost tracking. You can monitor API usage costs:
|
|
136
136
|
|
|
137
137
|
```typescript
|
|
138
|
-
//
|
|
138
|
+
// Get individual agent costs
|
|
139
139
|
const cost = agent.getUsageCost();
|
|
140
140
|
console.log("Usage cost:", cost);
|
|
141
141
|
|
|
142
|
-
// Get aggregated costs for all agents
|
|
143
|
-
const totalCosts =
|
|
142
|
+
// Get aggregated costs for all agents in the crew
|
|
143
|
+
const totalCosts = crew.getAggregatedCosts();
|
|
144
144
|
console.log("Total costs:", totalCosts);
|
|
145
|
+
|
|
146
|
+
// Reset cost tracking if needed
|
|
147
|
+
crew.resetCosts();
|
|
145
148
|
```
|
|
146
149
|
|
|
147
150
|
## Best Practices
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import { AxCrew } from "@amitdeshmukh/ax-crew";
|
|
2
2
|
|
|
3
3
|
// Example agent configuration
|
|
4
4
|
const agentConfig = {
|
|
@@ -10,18 +10,24 @@ const agentConfig = {
|
|
|
10
10
|
provider: "anthropic",
|
|
11
11
|
providerKeyName: "ANTHROPIC_API_KEY",
|
|
12
12
|
ai: {
|
|
13
|
-
model: "claude-3-
|
|
14
|
-
}
|
|
13
|
+
model: "claude-3-sonnet-20240229"
|
|
14
|
+
},
|
|
15
|
+
options: {
|
|
16
|
+
debug: true,
|
|
17
|
+
},
|
|
15
18
|
},
|
|
16
19
|
{
|
|
17
20
|
name: "writer",
|
|
18
21
|
description: "A writing agent that creates content",
|
|
19
|
-
signature: "topic:string
|
|
22
|
+
signature: "topic:string -> article:string",
|
|
20
23
|
provider: "anthropic",
|
|
21
24
|
providerKeyName: "ANTHROPIC_API_KEY",
|
|
22
25
|
ai: {
|
|
23
26
|
model: "claude-3-sonnet-20240229"
|
|
24
27
|
},
|
|
28
|
+
options: {
|
|
29
|
+
debug: true,
|
|
30
|
+
},
|
|
25
31
|
agents: ["researcher"] // This makes writer use researcher as a sub-agent
|
|
26
32
|
}
|
|
27
33
|
]
|
|
@@ -44,30 +50,23 @@ async function main() {
|
|
|
44
50
|
}
|
|
45
51
|
|
|
46
52
|
try {
|
|
47
|
-
// Use the researcher agent
|
|
48
|
-
const { research } = await researcher.forward({
|
|
49
|
-
query: "What are the key benefits of quantum computing?"
|
|
50
|
-
});
|
|
51
|
-
|
|
52
53
|
// Use the writer agent (which will internally use the researcher)
|
|
53
|
-
const article = await writer.forward({
|
|
54
|
+
const { article } = await writer.forward({
|
|
54
55
|
topic: "Quantum Computing Benefits",
|
|
55
|
-
research: research
|
|
56
56
|
});
|
|
57
57
|
|
|
58
58
|
// Print the article
|
|
59
59
|
console.log("Article:", article);
|
|
60
|
-
|
|
61
|
-
// Get costs for individual agents
|
|
62
|
-
const researcherCost = researcher.getUsageCost();
|
|
63
|
-
console.log("Researcher cost:", researcherCost);
|
|
64
60
|
|
|
65
|
-
//
|
|
66
|
-
|
|
67
|
-
console.log("
|
|
61
|
+
// Print usage costs
|
|
62
|
+
console.log("\nUsage:\n+++++++++++++++++++++++++++++++++");
|
|
63
|
+
console.log("Writer Agent:", JSON.stringify(writer.getAccumulatedCosts(), null, 2));
|
|
64
|
+
console.log("Researcher Agent Last Usage:", JSON.stringify(researcher.getLastUsageCost(), null, 2));
|
|
65
|
+
console.log("Researcher Agent Accumulated:", JSON.stringify(researcher.getAccumulatedCosts(), null, 2));
|
|
66
|
+
console.log("Total Cost:", JSON.stringify(crew.getAggregatedCosts(), null, 2));
|
|
68
67
|
|
|
69
68
|
// If you want to start fresh with cost tracking
|
|
70
|
-
|
|
69
|
+
crew.resetCosts();
|
|
71
70
|
|
|
72
71
|
} finally {
|
|
73
72
|
// Clean up
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AxCrew } from "
|
|
1
|
+
import { AxCrew } from "@amitdeshmukh/ax-crew";
|
|
2
2
|
|
|
3
3
|
// Define the crew configuration
|
|
4
4
|
const config = {
|
|
@@ -32,7 +32,7 @@ const config = {
|
|
|
32
32
|
temperature: 0,
|
|
33
33
|
},
|
|
34
34
|
options: {
|
|
35
|
-
debug:
|
|
35
|
+
debug: false,
|
|
36
36
|
},
|
|
37
37
|
agents: ["MathAgent"]
|
|
38
38
|
},
|
|
@@ -47,27 +47,26 @@ const agents = crew.addAgentsToCrew(["MathAgent", "ManagerAgent"]);
|
|
|
47
47
|
|
|
48
48
|
// Get agent instances
|
|
49
49
|
const managerAgent = agents?.get("ManagerAgent");
|
|
50
|
+
const mathAgent = agents?.get("MathAgent");
|
|
50
51
|
|
|
51
52
|
const userQuery: string =
|
|
52
53
|
"who is considered as the father of the iphone and what is the 7th root of their year of birth (precision to minimum 5 decimal places)";
|
|
53
54
|
console.log(`\n\nQuestion: ${userQuery}`);
|
|
54
55
|
|
|
55
56
|
const main = async (): Promise<void> => {
|
|
56
|
-
if (managerAgent) {
|
|
57
|
-
// Try with manager agent first
|
|
57
|
+
if (managerAgent && mathAgent) {
|
|
58
58
|
const managerResponse = await managerAgent.forward({
|
|
59
59
|
question: userQuery,
|
|
60
60
|
});
|
|
61
61
|
|
|
62
|
-
console.log(
|
|
63
|
-
`\nManager's Answer: ${JSON.stringify(managerResponse.answer, null, 2)}\n`
|
|
64
|
-
);
|
|
62
|
+
console.log(`\nAnswer: ${JSON.stringify(managerResponse.answer, null, 2)}`);
|
|
65
63
|
|
|
66
64
|
// Print usage costs
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
console.log("
|
|
70
|
-
console.log("
|
|
65
|
+
console.log("\nUsage:\n+++++++++++++++++++++++++++++++++");
|
|
66
|
+
console.log("Manager Agent:", JSON.stringify(managerAgent.getAccumulatedCosts(), null, 2));
|
|
67
|
+
console.log("Math Agent:", JSON.stringify(mathAgent.getLastUsageCost(), null, 2));
|
|
68
|
+
console.log("Math Agent Accumulated Costs:", JSON.stringify(mathAgent.getAccumulatedCosts(), null, 2));
|
|
69
|
+
console.log("Total Cost:", JSON.stringify(crew.getAggregatedCosts(), null, 2));
|
|
71
70
|
}
|
|
72
71
|
};
|
|
73
72
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import
|
|
1
|
+
// NOTE: For a more complete example of a crew that can research a topic, write a post and publish to WordPress, please see ./write-post-and-publish-to-wordpress.ts
|
|
2
|
+
|
|
3
|
+
import { AxCrew } from "@amitdeshmukh/ax-crew";
|
|
4
|
+
import type { FunctionRegistryType } from "@amitdeshmukh/ax-crew";
|
|
5
|
+
import { WordPressPost } from "@ax-crew/tools-wordpress";
|
|
4
6
|
|
|
5
7
|
// AxCrew configuration
|
|
6
8
|
const config = {
|
|
@@ -8,7 +10,7 @@ const config = {
|
|
|
8
10
|
{
|
|
9
11
|
name: "SearchQueryGenerator",
|
|
10
12
|
description: "Generates a list of google search queries to help research the topic",
|
|
11
|
-
signature: "topic:string \"The topic of the blog post\", guidance:string \"guidance from the user on what the blog post should be about\" -> googleSearchQueries:string[] \"an array of google search queries to help research the topic\"",
|
|
13
|
+
signature: "topic:string \"The topic of the blog post\", guidance:string \"guidance from the user on what the blog post should be about\" -> googleSearchQueries:string[] \"an array of upto 5 google search queries to help research the topic\"",
|
|
12
14
|
provider: "anthropic",
|
|
13
15
|
providerKeyName: "ANTHROPIC_API_KEY",
|
|
14
16
|
ai: {
|
|
@@ -27,12 +29,15 @@ const config = {
|
|
|
27
29
|
provider: "google-gemini",
|
|
28
30
|
providerKeyName: "GEMINI_API_KEY",
|
|
29
31
|
ai: {
|
|
30
|
-
model: "gemini-
|
|
32
|
+
model: "gemini-1.5-pro",
|
|
31
33
|
temperature: 0.5
|
|
32
34
|
},
|
|
33
35
|
options: {
|
|
34
36
|
debug: true,
|
|
35
|
-
stream: false
|
|
37
|
+
stream: false,
|
|
38
|
+
googleSearchRetrieval: {
|
|
39
|
+
mode: "MODE_UNSPECIFIED"
|
|
40
|
+
}
|
|
36
41
|
}
|
|
37
42
|
},
|
|
38
43
|
{
|
|
@@ -49,93 +54,83 @@ const config = {
|
|
|
49
54
|
debug: true,
|
|
50
55
|
stream: false
|
|
51
56
|
}
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: "WordPressPoster",
|
|
60
|
+
description: "Creates a post on WordPress with the given title, content and status",
|
|
61
|
+
signature: "blogPostTitle:string \"the title of the blog post\", blogPostContent:string \"the content of the blog post.\", status:string \"the status of the post (draft, publish, private)\" -> postResponse:string \"the response from the WordPress API\"",
|
|
62
|
+
provider: "anthropic",
|
|
63
|
+
providerKeyName: "ANTHROPIC_API_KEY",
|
|
64
|
+
ai: {
|
|
65
|
+
model: "claude-3-5-sonnet-20240620",
|
|
66
|
+
temperature: 0
|
|
67
|
+
},
|
|
68
|
+
options: {
|
|
69
|
+
debug: true,
|
|
70
|
+
stream: false
|
|
71
|
+
},
|
|
72
|
+
functions: ["WordPressPost"]
|
|
52
73
|
}
|
|
53
74
|
]
|
|
54
75
|
};
|
|
55
76
|
|
|
56
|
-
// Function to post to WordPress
|
|
57
|
-
async function postToWordPress(title: string, content: string, status: 'draft' | 'publish' = 'draft') {
|
|
58
|
-
const wpUrl = process.env.WORDPRESS_URL;
|
|
59
|
-
const wpUsername = process.env.WORDPRESS_USERNAME;
|
|
60
|
-
const wpPassword = process.env.WORDPRESS_PASSWORD;
|
|
61
|
-
|
|
62
|
-
if (!wpUrl) {
|
|
63
|
-
throw new Error('WordPress credentials not found in environment variables');
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const auth = Buffer.from(`${wpUsername}:${wpPassword}`).toString('base64');
|
|
67
|
-
|
|
68
|
-
const httpsAgent = new https.Agent({
|
|
69
|
-
rejectUnauthorized: false
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
try {
|
|
73
|
-
const response = await fetch(`${wpUrl}/wp-json/wp/v2/posts`, {
|
|
74
|
-
method: 'POST',
|
|
75
|
-
headers: {
|
|
76
|
-
'Authorization': `Basic ${auth}`,
|
|
77
|
-
'Content-Type': 'application/json'
|
|
78
|
-
},
|
|
79
|
-
body: JSON.stringify({
|
|
80
|
-
title,
|
|
81
|
-
content,
|
|
82
|
-
status
|
|
83
|
-
}),
|
|
84
|
-
agent: httpsAgent
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
if (!response.ok) {
|
|
88
|
-
const error = await response.text();
|
|
89
|
-
throw new Error(`WordPress API error: ${error}`);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const post = await response.json();
|
|
93
|
-
return {
|
|
94
|
-
id: post.id,
|
|
95
|
-
url: post.link,
|
|
96
|
-
status: post.status
|
|
97
|
-
};
|
|
98
|
-
} catch (error) {
|
|
99
|
-
console.error('Error details:', error);
|
|
100
|
-
throw new Error(`Failed to post to WordPress: ${error.message}`);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
77
|
const main = async () => {
|
|
105
78
|
// Create crew with type checking
|
|
106
|
-
const
|
|
79
|
+
const customFunctions: FunctionRegistryType = {
|
|
80
|
+
WordPressPost: WordPressPost
|
|
81
|
+
};
|
|
82
|
+
const crew = new AxCrew(config, customFunctions);
|
|
83
|
+
|
|
84
|
+
// Add agents to crew
|
|
85
|
+
const agents = crew.addAgentsToCrew([
|
|
86
|
+
'SearchQueryGenerator',
|
|
87
|
+
'GoogleSearch',
|
|
88
|
+
'BlogPostWriter',
|
|
89
|
+
'WordPressPoster'
|
|
90
|
+
]);
|
|
91
|
+
|
|
92
|
+
// NOTE: Your Wordpress site needs to have the Basic Auth plugin installed and enabled for the automatic posting to work
|
|
93
|
+
// Refer to https://github.com/WP-API/Basic-Auth?tab=readme-ov-file
|
|
94
|
+
|
|
95
|
+
// Set environment variables
|
|
96
|
+
crew.state.set("env", {
|
|
97
|
+
WORDPRESS_URL: "http://my-wordpress-site.com",
|
|
98
|
+
WORDPRESS_USERNAME: "my-username",
|
|
99
|
+
WORDPRESS_PASSWORD: "my-password"
|
|
100
|
+
});
|
|
107
101
|
|
|
108
|
-
//
|
|
109
|
-
const agents = crew.addAgentsToCrew(['SearchQueryGenerator', 'GoogleSearch', 'BlogPostWriter']);
|
|
102
|
+
// Get agents from crew
|
|
110
103
|
const planner = agents?.get('SearchQueryGenerator');
|
|
111
104
|
const googleSearch = agents?.get('GoogleSearch');
|
|
112
105
|
const writer = agents?.get('BlogPostWriter');
|
|
106
|
+
const poster = agents?.get('WordPressPoster');
|
|
113
107
|
|
|
114
|
-
|
|
115
|
-
const
|
|
108
|
+
// Define the topic and guidance
|
|
109
|
+
const topic = "How to tell what your dog is thinking";
|
|
110
|
+
const guidance = "The article should be a fun and engaging article and less that 500 words long. It should help the reader to understand their dog better.";
|
|
116
111
|
|
|
117
|
-
if (planner && googleSearch && writer) {
|
|
118
|
-
//
|
|
112
|
+
if (planner && googleSearch && writer && poster) {
|
|
113
|
+
// Generate search queries
|
|
119
114
|
const plannerResponse = await planner.forward({ topic, guidance });
|
|
120
115
|
const { googleSearchQueries } = plannerResponse;
|
|
121
116
|
|
|
117
|
+
// Research the topic
|
|
122
118
|
const googleSearchResults: string[] = [];
|
|
123
119
|
for (const query of googleSearchQueries) {
|
|
124
120
|
const googleSearchResponse = await googleSearch.forward({ googleSearchQuery: query });
|
|
125
121
|
const { googleSearchResult } = googleSearchResponse;
|
|
126
122
|
googleSearchResults.push(googleSearchResult);
|
|
123
|
+
// Wait for 3 seconds between queries to avoid rate limiting
|
|
124
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
127
125
|
}
|
|
128
126
|
|
|
129
|
-
|
|
127
|
+
// Write the blog post
|
|
130
128
|
const writerResponse = await writer.forward({ topic, guidance, googleSearchResults });
|
|
131
129
|
const { blogPostTitle, blogPostContent } = writerResponse;
|
|
132
130
|
|
|
133
|
-
console.log(blogPostTitle);
|
|
134
|
-
console.log(blogPostContent);
|
|
135
|
-
|
|
136
131
|
// Post to WordPress
|
|
137
132
|
try {
|
|
138
|
-
const postResponse = await
|
|
133
|
+
const postResponse = await poster.forward({ blogPostTitle, blogPostContent, status: "publish" });
|
|
139
134
|
console.log('Successfully posted to WordPress:', postResponse);
|
|
140
135
|
} catch (error) {
|
|
141
136
|
console.error('Failed to post to WordPress:', error);
|
package/index.d.ts
CHANGED
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
import { AxAI, AxAgentic, AxFunction, AxSignature, AxProgramForwardOptions, AxModelConfig, AxAgent } from "@ax-llm/ax";
|
|
2
|
+
import type { UsageCost, AggregatedCosts } from "./src/agents/agentUseCosts.js";
|
|
2
3
|
|
|
3
|
-
export
|
|
4
|
-
promptCost: string;
|
|
5
|
-
completionCost: string;
|
|
6
|
-
totalCost: string;
|
|
7
|
-
tokenMetrics: {
|
|
8
|
-
promptTokens: number;
|
|
9
|
-
completionTokens: number;
|
|
10
|
-
totalTokens: number;
|
|
11
|
-
};
|
|
12
|
-
}
|
|
4
|
+
export { UsageCost, AggregatedCosts };
|
|
13
5
|
|
|
14
6
|
export interface StateInstance {
|
|
15
7
|
reset(): void;
|
|
@@ -83,6 +75,8 @@ export class AxCrew {
|
|
|
83
75
|
createAgent(agentName: string): StatefulAxAgent;
|
|
84
76
|
addAgent(agentName: string): void;
|
|
85
77
|
addAgentsToCrew(agentNames: string[]): Map<string, StatefulAxAgent> | null;
|
|
78
|
+
getAggregatedCosts(): AggregatedCosts;
|
|
79
|
+
resetCosts(): void;
|
|
86
80
|
destroy(): void;
|
|
87
81
|
}
|
|
88
82
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@amitdeshmukh/ax-crew",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.6.0",
|
|
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",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"uuid": "^10.0.0"
|
|
20
20
|
},
|
|
21
21
|
"peerDependencies": {
|
|
22
|
-
"@ax-llm/ax": "^
|
|
22
|
+
"@ax-llm/ax": "^11.0.21"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@types/node": "^20.14.9",
|
|
@@ -22,6 +22,20 @@ export interface UsageCost {
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
export interface AggregatedMetrics {
|
|
26
|
+
promptTokens: number;
|
|
27
|
+
completionTokens: number;
|
|
28
|
+
totalTokens: number;
|
|
29
|
+
promptCost: string;
|
|
30
|
+
completionCost: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface AggregatedCosts {
|
|
34
|
+
totalCost: string;
|
|
35
|
+
byAgent: Record<string, UsageCost>;
|
|
36
|
+
aggregatedMetrics: AggregatedMetrics;
|
|
37
|
+
}
|
|
38
|
+
|
|
25
39
|
// Utility class to handle usage related functionality
|
|
26
40
|
export class StateFulAxAgentUsage {
|
|
27
41
|
static STATE_KEY_PREFIX = 'agent_usage_';
|
|
@@ -78,17 +92,7 @@ export class StateFulAxAgentUsage {
|
|
|
78
92
|
}
|
|
79
93
|
}
|
|
80
94
|
|
|
81
|
-
static getAggregatedCosts(state: StateInstance): {
|
|
82
|
-
totalCost: string;
|
|
83
|
-
byAgent: Record<string, UsageCost>;
|
|
84
|
-
aggregatedMetrics: {
|
|
85
|
-
promptTokens: number;
|
|
86
|
-
completionTokens: number;
|
|
87
|
-
totalTokens: number;
|
|
88
|
-
promptCost: string;
|
|
89
|
-
completionCost: string;
|
|
90
|
-
};
|
|
91
|
-
} {
|
|
95
|
+
static getAggregatedCosts(state: StateInstance): AggregatedCosts {
|
|
92
96
|
const allState = state.getAll();
|
|
93
97
|
const agentCosts: Record<string, UsageCost> = {};
|
|
94
98
|
let totalPromptTokens = 0;
|
package/src/agents/index.ts
CHANGED
|
@@ -59,7 +59,7 @@ class StatefulAxAgent extends AxAgent<any, any> {
|
|
|
59
59
|
|
|
60
60
|
// Set examples if provided
|
|
61
61
|
if (examples && examples.length > 0) {
|
|
62
|
-
|
|
62
|
+
super.setExamples(examples);
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
|
|
@@ -73,17 +73,20 @@ class StatefulAxAgent extends AxAgent<any, any> {
|
|
|
73
73
|
second?: Record<string, any> | Readonly<AxProgramForwardOptions>,
|
|
74
74
|
third?: Readonly<AxProgramForwardOptions>
|
|
75
75
|
): Promise<Record<string, any>> {
|
|
76
|
-
// Sub-agent case (called with AI service)
|
|
77
76
|
let result;
|
|
77
|
+
|
|
78
|
+
// Track costs regardless of whether it's a direct or sub-agent call
|
|
79
|
+
// This ensures we capture multiple legitimate calls to the same agent
|
|
78
80
|
if ('apiURL' in first) {
|
|
81
|
+
// Sub-agent case (called with AI service)
|
|
79
82
|
result = await super.forward(this.axai, second as Record<string, any>, third);
|
|
80
83
|
} else {
|
|
81
84
|
// Direct call case
|
|
82
85
|
result = await super.forward(this.axai, first, second as Readonly<AxProgramForwardOptions>);
|
|
83
86
|
}
|
|
84
87
|
|
|
85
|
-
// Track costs
|
|
86
|
-
const cost = this.
|
|
88
|
+
// Track costs after the call
|
|
89
|
+
const cost = this.getLastUsageCost();
|
|
87
90
|
if (cost) {
|
|
88
91
|
StateFulAxAgentUsage.trackCostInState(this.agentName, cost, this.state);
|
|
89
92
|
}
|
|
@@ -91,10 +94,10 @@ class StatefulAxAgent extends AxAgent<any, any> {
|
|
|
91
94
|
return result;
|
|
92
95
|
}
|
|
93
96
|
|
|
94
|
-
// Get the usage cost for
|
|
95
|
-
|
|
96
|
-
const { modelUsage, modelInfo,
|
|
97
|
-
const currentModelInfo = modelInfo?.find((m: { name: string }) => m.name ===
|
|
97
|
+
// Get the usage cost for the most recent run of the agent
|
|
98
|
+
getLastUsageCost(): UsageCost | null {
|
|
99
|
+
const { modelUsage, modelInfo, defaults } = this.axai;
|
|
100
|
+
const currentModelInfo = modelInfo?.find((m: { name: string }) => m.name === defaults.model);
|
|
98
101
|
|
|
99
102
|
if (!currentModelInfo || !modelUsage) {
|
|
100
103
|
return null;
|
|
@@ -103,14 +106,10 @@ class StatefulAxAgent extends AxAgent<any, any> {
|
|
|
103
106
|
return StateFulAxAgentUsage.calculateCost(modelUsage, currentModelInfo);
|
|
104
107
|
}
|
|
105
108
|
|
|
106
|
-
// Get
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
// Reset all cost tracking
|
|
112
|
-
resetCosts(): void {
|
|
113
|
-
StateFulAxAgentUsage.resetCosts(this.state);
|
|
109
|
+
// Get the accumulated costs for all runs of this agent
|
|
110
|
+
getAccumulatedCosts(): UsageCost | null {
|
|
111
|
+
const stateKey = `${StateFulAxAgentUsage.STATE_KEY_PREFIX}${this.agentName}`;
|
|
112
|
+
return this.state.get(stateKey) as UsageCost | null;
|
|
114
113
|
}
|
|
115
114
|
}
|
|
116
115
|
|
|
@@ -235,6 +234,21 @@ class AxCrew {
|
|
|
235
234
|
this.agents = null;
|
|
236
235
|
this.state.reset();
|
|
237
236
|
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Gets aggregated costs for all agents in the crew
|
|
240
|
+
* @returns Aggregated cost information for all agents
|
|
241
|
+
*/
|
|
242
|
+
getAggregatedCosts(): ReturnType<typeof StateFulAxAgentUsage.getAggregatedCosts> {
|
|
243
|
+
return StateFulAxAgentUsage.getAggregatedCosts(this.state);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Resets all cost tracking for the crew
|
|
248
|
+
*/
|
|
249
|
+
resetCosts(): void {
|
|
250
|
+
StateFulAxAgentUsage.resetCosts(this.state);
|
|
251
|
+
}
|
|
238
252
|
}
|
|
239
253
|
|
|
240
254
|
export { AxCrew };
|