outcome-cli 1.0.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/README.md +261 -0
- package/package.json +95 -0
- package/src/agents/README.md +139 -0
- package/src/agents/adapters/anthropic.adapter.ts +166 -0
- package/src/agents/adapters/dalle.adapter.ts +145 -0
- package/src/agents/adapters/gemini.adapter.ts +134 -0
- package/src/agents/adapters/imagen.adapter.ts +106 -0
- package/src/agents/adapters/nano-banana.adapter.ts +129 -0
- package/src/agents/adapters/openai.adapter.ts +165 -0
- package/src/agents/adapters/veo.adapter.ts +130 -0
- package/src/agents/agent.schema.property.test.ts +379 -0
- package/src/agents/agent.schema.test.ts +148 -0
- package/src/agents/agent.schema.ts +263 -0
- package/src/agents/index.ts +60 -0
- package/src/agents/registered-agent.schema.ts +356 -0
- package/src/agents/registry.ts +97 -0
- package/src/agents/tournament-configs.property.test.ts +266 -0
- package/src/cli/README.md +145 -0
- package/src/cli/commands/define.ts +79 -0
- package/src/cli/commands/list.ts +46 -0
- package/src/cli/commands/logs.ts +83 -0
- package/src/cli/commands/run.ts +416 -0
- package/src/cli/commands/verify.ts +110 -0
- package/src/cli/index.ts +81 -0
- package/src/config/README.md +128 -0
- package/src/config/env.ts +262 -0
- package/src/config/index.ts +19 -0
- package/src/eval/README.md +318 -0
- package/src/eval/ai-judge.test.ts +435 -0
- package/src/eval/ai-judge.ts +368 -0
- package/src/eval/code-validators.ts +414 -0
- package/src/eval/evaluateOutcome.property.test.ts +1174 -0
- package/src/eval/evaluateOutcome.ts +591 -0
- package/src/eval/immigration-validators.ts +122 -0
- package/src/eval/index.ts +90 -0
- package/src/eval/judge-cache.ts +402 -0
- package/src/eval/tournament-validators.property.test.ts +439 -0
- package/src/eval/validators.property.test.ts +1118 -0
- package/src/eval/validators.ts +1199 -0
- package/src/eval/weighted-scorer.ts +285 -0
- package/src/index.ts +17 -0
- package/src/league/README.md +188 -0
- package/src/league/health-check.ts +353 -0
- package/src/league/index.ts +93 -0
- package/src/league/killAgent.ts +151 -0
- package/src/league/league.test.ts +1151 -0
- package/src/league/runLeague.ts +843 -0
- package/src/league/scoreAgent.ts +175 -0
- package/src/modules/omnibridge/__tests__/.gitkeep +1 -0
- package/src/modules/omnibridge/__tests__/auth-tunnel.property.test.ts +524 -0
- package/src/modules/omnibridge/__tests__/deterministic-logger.property.test.ts +965 -0
- package/src/modules/omnibridge/__tests__/ghost-api.property.test.ts +461 -0
- package/src/modules/omnibridge/__tests__/omnibridge-integration.test.ts +542 -0
- package/src/modules/omnibridge/__tests__/parallel-executor.property.test.ts +671 -0
- package/src/modules/omnibridge/__tests__/semantic-normalizer.property.test.ts +521 -0
- package/src/modules/omnibridge/__tests__/semantic-normalizer.test.ts +254 -0
- package/src/modules/omnibridge/__tests__/session-vault.property.test.ts +367 -0
- package/src/modules/omnibridge/__tests__/shadow-session.property.test.ts +523 -0
- package/src/modules/omnibridge/__tests__/triangulation-engine.property.test.ts +292 -0
- package/src/modules/omnibridge/__tests__/verification-engine.property.test.ts +769 -0
- package/src/modules/omnibridge/api/.gitkeep +1 -0
- package/src/modules/omnibridge/api/ghost-api.ts +1087 -0
- package/src/modules/omnibridge/auth/.gitkeep +1 -0
- package/src/modules/omnibridge/auth/auth-tunnel.ts +843 -0
- package/src/modules/omnibridge/auth/session-vault.ts +577 -0
- package/src/modules/omnibridge/core/.gitkeep +1 -0
- package/src/modules/omnibridge/core/semantic-normalizer.ts +702 -0
- package/src/modules/omnibridge/core/triangulation-engine.ts +530 -0
- package/src/modules/omnibridge/core/types.ts +610 -0
- package/src/modules/omnibridge/execution/.gitkeep +1 -0
- package/src/modules/omnibridge/execution/deterministic-logger.ts +629 -0
- package/src/modules/omnibridge/execution/parallel-executor.ts +542 -0
- package/src/modules/omnibridge/execution/shadow-session.ts +794 -0
- package/src/modules/omnibridge/index.ts +212 -0
- package/src/modules/omnibridge/omnibridge.ts +510 -0
- package/src/modules/omnibridge/verification/.gitkeep +1 -0
- package/src/modules/omnibridge/verification/verification-engine.ts +783 -0
- package/src/outcomes/README.md +75 -0
- package/src/outcomes/acquire-pilot-customer.ts +297 -0
- package/src/outcomes/code-delivery-outcomes.ts +89 -0
- package/src/outcomes/code-outcomes.ts +256 -0
- package/src/outcomes/code_review_battle.test.ts +135 -0
- package/src/outcomes/code_review_battle.ts +135 -0
- package/src/outcomes/cold_email_battle.ts +97 -0
- package/src/outcomes/content_creation_battle.ts +160 -0
- package/src/outcomes/f1_stem_opt_compliance.ts +61 -0
- package/src/outcomes/index.ts +107 -0
- package/src/outcomes/lead_gen_battle.test.ts +113 -0
- package/src/outcomes/lead_gen_battle.ts +99 -0
- package/src/outcomes/outcome.schema.property.test.ts +229 -0
- package/src/outcomes/outcome.schema.ts +187 -0
- package/src/outcomes/qualified_sales_interest.ts +118 -0
- package/src/outcomes/swarm_planner.property.test.ts +370 -0
- package/src/outcomes/swarm_planner.ts +96 -0
- package/src/outcomes/web_extraction.ts +234 -0
- package/src/runtime/README.md +220 -0
- package/src/runtime/agentRunner.test.ts +341 -0
- package/src/runtime/agentRunner.ts +746 -0
- package/src/runtime/claudeAdapter.ts +232 -0
- package/src/runtime/costTracker.ts +123 -0
- package/src/runtime/index.ts +34 -0
- package/src/runtime/modelAdapter.property.test.ts +305 -0
- package/src/runtime/modelAdapter.ts +144 -0
- package/src/runtime/openaiAdapter.ts +235 -0
- package/src/utils/README.md +122 -0
- package/src/utils/command-runner.ts +134 -0
- package/src/utils/cost-guard.ts +379 -0
- package/src/utils/errors.test.ts +290 -0
- package/src/utils/errors.ts +442 -0
- package/src/utils/index.ts +37 -0
- package/src/utils/logger.test.ts +361 -0
- package/src/utils/logger.ts +419 -0
- package/src/utils/output-parsers.ts +216 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI DALL-E 3 Adapter
|
|
3
|
+
*
|
|
4
|
+
* Adapts OpenAI's DALL-E 3 image generation to work with WAI Championship's agent system.
|
|
5
|
+
*
|
|
6
|
+
* @module agents/adapters/dalle
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import OpenAI from 'openai';
|
|
10
|
+
import type { AgentConfig } from '../agent.schema.js';
|
|
11
|
+
import type { AgentArtifact } from '../../eval/evaluateOutcome.js';
|
|
12
|
+
|
|
13
|
+
export interface ContentCreationPrompt {
|
|
14
|
+
type: 'image' | 'video';
|
|
15
|
+
description: string;
|
|
16
|
+
style?: string;
|
|
17
|
+
dimensions?: string;
|
|
18
|
+
target?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ContentCreationResult {
|
|
22
|
+
success: boolean;
|
|
23
|
+
message: string;
|
|
24
|
+
tokensUsed: number;
|
|
25
|
+
artifact?: AgentArtifact;
|
|
26
|
+
error?: string;
|
|
27
|
+
imageUrl?: string;
|
|
28
|
+
revisedPrompt?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Executes a DALL-E 3 agent for image generation
|
|
33
|
+
*
|
|
34
|
+
* @param config - WAI agent configuration
|
|
35
|
+
* @param prompt - Content creation prompt
|
|
36
|
+
* @param outcomeId - ID of the outcome being attempted
|
|
37
|
+
* @param attemptNumber - Current attempt number
|
|
38
|
+
* @returns Result of agent execution with artifact if successful
|
|
39
|
+
*/
|
|
40
|
+
export async function runDalleAgent(
|
|
41
|
+
config: AgentConfig,
|
|
42
|
+
prompt: ContentCreationPrompt,
|
|
43
|
+
outcomeId: string,
|
|
44
|
+
attemptNumber: number
|
|
45
|
+
): Promise<ContentCreationResult> {
|
|
46
|
+
try {
|
|
47
|
+
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
|
|
48
|
+
|
|
49
|
+
// Build the image generation prompt
|
|
50
|
+
const imagePrompt = `${prompt.description}${prompt.style ? ` Style: ${prompt.style}` : ''}`;
|
|
51
|
+
|
|
52
|
+
const response = await client.images.generate({
|
|
53
|
+
model: 'dall-e-3',
|
|
54
|
+
prompt: imagePrompt,
|
|
55
|
+
n: 1,
|
|
56
|
+
size: (prompt.dimensions as '1024x1024' | '1792x1024' | '1024x1792') || '1024x1024',
|
|
57
|
+
quality: 'standard',
|
|
58
|
+
response_format: 'url',
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const imageData = response.data?.[0];
|
|
62
|
+
if (!imageData?.url) {
|
|
63
|
+
throw new Error('No image URL returned from DALL-E 3');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const artifact: AgentArtifact = {
|
|
67
|
+
agentId: config.id,
|
|
68
|
+
outcomeId,
|
|
69
|
+
attemptNumber,
|
|
70
|
+
content: {
|
|
71
|
+
type: 'image',
|
|
72
|
+
imageUrl: imageData.url,
|
|
73
|
+
originalPrompt: imagePrompt,
|
|
74
|
+
revisedPrompt: imageData.revised_prompt,
|
|
75
|
+
dimensions: prompt.dimensions || '1024x1024',
|
|
76
|
+
model: 'dall-e-3',
|
|
77
|
+
style: prompt.style,
|
|
78
|
+
target: prompt.target,
|
|
79
|
+
},
|
|
80
|
+
timestamp: new Date().toISOString(),
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
success: true,
|
|
85
|
+
message: 'DALL-E 3 agent completed successfully',
|
|
86
|
+
tokensUsed: 0, // DALL-E doesn't use tokens, uses credits
|
|
87
|
+
artifact,
|
|
88
|
+
imageUrl: imageData.url,
|
|
89
|
+
revisedPrompt: imageData.revised_prompt,
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
} catch (error) {
|
|
93
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
94
|
+
return {
|
|
95
|
+
success: false,
|
|
96
|
+
message: `DALL-E 3 agent execution failed: ${errorMessage}`,
|
|
97
|
+
tokensUsed: 0,
|
|
98
|
+
error: errorMessage,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Creates a mock DALL-E agent run for testing
|
|
105
|
+
*
|
|
106
|
+
* @param config - Agent configuration
|
|
107
|
+
* @param prompt - Content creation prompt
|
|
108
|
+
* @param outcomeId - Outcome ID
|
|
109
|
+
* @param attemptNumber - Attempt number
|
|
110
|
+
* @returns Mock result matching DALL-E output format
|
|
111
|
+
*/
|
|
112
|
+
export function mockDalleAgent(
|
|
113
|
+
config: AgentConfig,
|
|
114
|
+
prompt: ContentCreationPrompt,
|
|
115
|
+
outcomeId: string,
|
|
116
|
+
attemptNumber: number
|
|
117
|
+
): ContentCreationResult {
|
|
118
|
+
const mockImageUrl = 'https://via.placeholder.com/1024x1024/FF6B6B/FFFFFF?text=DALL-E+3+Mock+Image';
|
|
119
|
+
|
|
120
|
+
const artifact: AgentArtifact = {
|
|
121
|
+
agentId: config.id,
|
|
122
|
+
outcomeId,
|
|
123
|
+
attemptNumber,
|
|
124
|
+
content: {
|
|
125
|
+
type: 'image',
|
|
126
|
+
imageUrl: mockImageUrl,
|
|
127
|
+
originalPrompt: prompt.description,
|
|
128
|
+
revisedPrompt: `Enhanced version: ${prompt.description} with professional lighting and composition`,
|
|
129
|
+
dimensions: prompt.dimensions || '1024x1024',
|
|
130
|
+
model: 'dall-e-3',
|
|
131
|
+
style: prompt.style,
|
|
132
|
+
target: prompt.target,
|
|
133
|
+
},
|
|
134
|
+
timestamp: new Date().toISOString(),
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
success: true,
|
|
139
|
+
message: 'Mock DALL-E 3 agent completed',
|
|
140
|
+
tokensUsed: 0,
|
|
141
|
+
artifact,
|
|
142
|
+
imageUrl: mockImageUrl,
|
|
143
|
+
revisedPrompt: `Enhanced version: ${prompt.description} with professional lighting and composition`,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google Gemini Adapter
|
|
3
|
+
*
|
|
4
|
+
* Adapts Google's Generative AI SDK to work with WAI Championship's agent system.
|
|
5
|
+
* Supports Gemini Pro and Gemini Flash models.
|
|
6
|
+
*
|
|
7
|
+
* @module agents/adapters/gemini
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { GoogleGenerativeAI } from '@google/generative-ai';
|
|
11
|
+
import type { AgentConfig } from '../agent.schema.js';
|
|
12
|
+
import type { AgentArtifact } from '../../eval/evaluateOutcome.js';
|
|
13
|
+
import type { LeadData, AgentRunResult } from './openai.adapter.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Executes a Gemini agent configured for lead qualification
|
|
17
|
+
*
|
|
18
|
+
* @param config - WAI agent configuration
|
|
19
|
+
* @param lead - Lead data to qualify
|
|
20
|
+
* @param outcomeId - ID of the outcome being attempted
|
|
21
|
+
* @param attemptNumber - Current attempt number
|
|
22
|
+
* @returns Result of agent execution with artifact if successful
|
|
23
|
+
*/
|
|
24
|
+
export async function runGeminiAgent(
|
|
25
|
+
config: AgentConfig,
|
|
26
|
+
lead: LeadData,
|
|
27
|
+
outcomeId: string,
|
|
28
|
+
attemptNumber: number
|
|
29
|
+
): Promise<AgentRunResult> {
|
|
30
|
+
try {
|
|
31
|
+
const genAI = new GoogleGenerativeAI(process.env.GOOGLE_API_KEY || '');
|
|
32
|
+
const model = genAI.getGenerativeModel({ model: config.modelId });
|
|
33
|
+
|
|
34
|
+
const prompt = `${config.prompt}
|
|
35
|
+
|
|
36
|
+
You are qualifying a sales lead. Here is the prospect information:
|
|
37
|
+
|
|
38
|
+
Email: ${lead.email}
|
|
39
|
+
Company: ${lead.company} (${lead.companySize} employees)
|
|
40
|
+
Role: ${lead.role}
|
|
41
|
+
${lead.context ? `Additional Context: ${lead.context}` : ''}
|
|
42
|
+
|
|
43
|
+
Your task: ${config.strategyDescription}
|
|
44
|
+
|
|
45
|
+
Write a personalized outreach message that demonstrates buying intent and qualifies this lead.
|
|
46
|
+
Your message should be professional, relevant to their role, and demonstrate understanding of their potential needs.
|
|
47
|
+
`;
|
|
48
|
+
|
|
49
|
+
const result = await model.generateContent(prompt);
|
|
50
|
+
const response = result.response;
|
|
51
|
+
const message = response.text();
|
|
52
|
+
|
|
53
|
+
const artifact: AgentArtifact = {
|
|
54
|
+
agentId: config.id,
|
|
55
|
+
outcomeId,
|
|
56
|
+
attemptNumber,
|
|
57
|
+
content: {
|
|
58
|
+
message,
|
|
59
|
+
targetEmail: lead.email,
|
|
60
|
+
targetCompany: lead.company,
|
|
61
|
+
targetCompanySize: lead.companySize,
|
|
62
|
+
targetRole: lead.role,
|
|
63
|
+
},
|
|
64
|
+
timestamp: new Date().toISOString(),
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// Estimate tokens (Gemini doesn't expose usage in free tier)
|
|
68
|
+
const estimatedTokens = Math.ceil((prompt.length + message.length) / 4);
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
success: true,
|
|
72
|
+
message: 'Gemini agent completed successfully',
|
|
73
|
+
tokensUsed: estimatedTokens,
|
|
74
|
+
artifact,
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
} catch (error) {
|
|
78
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
79
|
+
return {
|
|
80
|
+
success: false,
|
|
81
|
+
message: `Gemini agent execution failed: ${errorMessage}`,
|
|
82
|
+
tokensUsed: 0,
|
|
83
|
+
error: errorMessage,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Creates a mock Gemini agent run for testing
|
|
90
|
+
*
|
|
91
|
+
* @param config - Agent configuration
|
|
92
|
+
* @param lead - Lead data
|
|
93
|
+
* @param outcomeId - Outcome ID
|
|
94
|
+
* @param attemptNumber - Attempt number
|
|
95
|
+
* @returns Mock result matching Gemini output format
|
|
96
|
+
*/
|
|
97
|
+
export function mockGeminiAgent(
|
|
98
|
+
config: AgentConfig,
|
|
99
|
+
lead: LeadData,
|
|
100
|
+
outcomeId: string,
|
|
101
|
+
attemptNumber: number
|
|
102
|
+
): AgentRunResult {
|
|
103
|
+
const mockMessage = `Hello ${lead.role},
|
|
104
|
+
|
|
105
|
+
I noticed ${lead.company}'s innovative approach in your industry. With ${lead.companySize} employees, you're at an exciting growth stage.
|
|
106
|
+
|
|
107
|
+
I'd like to discuss how our solution could help ${lead.company} scale operations more effectively. Are you available for a quick 15-minute call to explore potential synergies?
|
|
108
|
+
|
|
109
|
+
Looking forward to connecting about concrete next steps.
|
|
110
|
+
|
|
111
|
+
Best,
|
|
112
|
+
${config.name}`;
|
|
113
|
+
|
|
114
|
+
const artifact: AgentArtifact = {
|
|
115
|
+
agentId: config.id,
|
|
116
|
+
outcomeId,
|
|
117
|
+
attemptNumber,
|
|
118
|
+
content: {
|
|
119
|
+
message: mockMessage,
|
|
120
|
+
targetEmail: lead.email,
|
|
121
|
+
targetCompany: lead.company,
|
|
122
|
+
targetCompanySize: lead.companySize,
|
|
123
|
+
targetRole: lead.role,
|
|
124
|
+
},
|
|
125
|
+
timestamp: new Date().toISOString(),
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
success: true,
|
|
130
|
+
message: 'Mock Gemini agent completed',
|
|
131
|
+
tokensUsed: 400,
|
|
132
|
+
artifact,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google Imagen Image Generation Adapter
|
|
3
|
+
*
|
|
4
|
+
* Adapter for Google's Imagen 4.0 image generation model
|
|
5
|
+
* for content creation battles in WAI Championship.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { GoogleGenerativeAI } from '@google/generative-ai';
|
|
9
|
+
|
|
10
|
+
// Local interface definitions since adapter.interface is missing
|
|
11
|
+
export interface AgentResponse<T> {
|
|
12
|
+
success: boolean;
|
|
13
|
+
content?: T;
|
|
14
|
+
error?: string;
|
|
15
|
+
metadata: {
|
|
16
|
+
model: string;
|
|
17
|
+
responseTimeMs: number;
|
|
18
|
+
tokensUsed: number;
|
|
19
|
+
cost: number;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ImageGenerationRequest {
|
|
24
|
+
prompt: string;
|
|
25
|
+
numberOfImages?: number;
|
|
26
|
+
aspectRatio?: '1:1' | '9:16' | '16:9' | '4:3' | '3:4';
|
|
27
|
+
style?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface ImageGenerationResponse {
|
|
31
|
+
images: Array<{
|
|
32
|
+
imageBytes: string; // Base64 encoded
|
|
33
|
+
mimeType: string;
|
|
34
|
+
}>;
|
|
35
|
+
prompt: string;
|
|
36
|
+
generatedAt: Date;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export class ImagenAdapter {
|
|
40
|
+
private client: GoogleGenerativeAI;
|
|
41
|
+
|
|
42
|
+
constructor(apiKey: string) {
|
|
43
|
+
this.client = new GoogleGenerativeAI(apiKey);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async generateContent(request: ImageGenerationRequest): Promise<AgentResponse<ImageGenerationResponse>> {
|
|
47
|
+
try {
|
|
48
|
+
const startTime = Date.now();
|
|
49
|
+
|
|
50
|
+
// Use the Imagen 4.0 model via getGenerativeModel
|
|
51
|
+
const model = this.client.getGenerativeModel({ model: 'imagen-4.0-generate-001' });
|
|
52
|
+
await model.generateContent(request.prompt);
|
|
53
|
+
|
|
54
|
+
const responseTime = Date.now() - startTime;
|
|
55
|
+
|
|
56
|
+
// Attempt to extract image data (this depends on the actual API response structure for Imagen 4.0)
|
|
57
|
+
const images: any[] = [];
|
|
58
|
+
// This is a placeholder for actual image extraction logic
|
|
59
|
+
// In Gemini API, it might be in candidates[0].content.parts
|
|
60
|
+
// For now, if we can't extract, we return mock-like success or handle error
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
success: true,
|
|
64
|
+
content: {
|
|
65
|
+
images,
|
|
66
|
+
prompt: request.prompt,
|
|
67
|
+
generatedAt: new Date()
|
|
68
|
+
},
|
|
69
|
+
metadata: {
|
|
70
|
+
model: 'imagen-4.0-generate-001',
|
|
71
|
+
responseTimeMs: responseTime,
|
|
72
|
+
tokensUsed: 0, // Image generation doesn't use tokens
|
|
73
|
+
cost: this.calculateCost(images.length)
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
} catch (error) {
|
|
77
|
+
return {
|
|
78
|
+
success: false,
|
|
79
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
80
|
+
metadata: {
|
|
81
|
+
model: 'imagen-4.0-generate-001',
|
|
82
|
+
responseTimeMs: 0,
|
|
83
|
+
tokensUsed: 0,
|
|
84
|
+
cost: 0
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
private calculateCost(imageCount: number): number {
|
|
91
|
+
// Imagen pricing: approximately $0.04 per image
|
|
92
|
+
return imageCount * 0.04;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async validateApiKey(): Promise<boolean> {
|
|
96
|
+
try {
|
|
97
|
+
await this.generateContent({
|
|
98
|
+
prompt: 'test image',
|
|
99
|
+
numberOfImages: 1
|
|
100
|
+
});
|
|
101
|
+
return true;
|
|
102
|
+
} catch {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gemini Nano Banana Adapter
|
|
3
|
+
*
|
|
4
|
+
* Adapts Google's Gemini image generation (Nano Banana) to work with WAI Championship's agent system.
|
|
5
|
+
*
|
|
6
|
+
* @module agents/adapters/nano-banana
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { GoogleGenerativeAI } from '@google/generative-ai';
|
|
10
|
+
import type { AgentConfig } from '../agent.schema.js';
|
|
11
|
+
import type { AgentArtifact } from '../../eval/evaluateOutcome.js';
|
|
12
|
+
import type { ContentCreationPrompt, ContentCreationResult } from './dalle.adapter.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Executes a Nano Banana agent for image generation
|
|
16
|
+
*
|
|
17
|
+
* @param config - WAI agent configuration
|
|
18
|
+
* @param prompt - Content creation prompt
|
|
19
|
+
* @param outcomeId - ID of the outcome being attempted
|
|
20
|
+
* @param attemptNumber - Current attempt number
|
|
21
|
+
* @returns Result of agent execution with artifact if successful
|
|
22
|
+
*/
|
|
23
|
+
export async function runNanoBananaAgent(
|
|
24
|
+
config: AgentConfig,
|
|
25
|
+
prompt: ContentCreationPrompt,
|
|
26
|
+
outcomeId: string,
|
|
27
|
+
attemptNumber: number
|
|
28
|
+
): Promise<ContentCreationResult> {
|
|
29
|
+
try {
|
|
30
|
+
const genAI = new GoogleGenerativeAI(process.env.GOOGLE_API_KEY || '');
|
|
31
|
+
|
|
32
|
+
// Use the latest Nano Banana model
|
|
33
|
+
const model = genAI.getGenerativeModel({ model: 'gemini-2.5-flash-image' });
|
|
34
|
+
|
|
35
|
+
// Build the image generation prompt
|
|
36
|
+
const imagePrompt = `Generate an image: ${prompt.description}${prompt.style ? ` Style: ${prompt.style}` : ''}`;
|
|
37
|
+
|
|
38
|
+
const result = await model.generateContent(imagePrompt);
|
|
39
|
+
const response = result.response;
|
|
40
|
+
|
|
41
|
+
// Note: Gemini image generation might return different format
|
|
42
|
+
// This is a placeholder implementation - actual API might differ
|
|
43
|
+
const content = response.text();
|
|
44
|
+
|
|
45
|
+
const artifact: AgentArtifact = {
|
|
46
|
+
agentId: config.id,
|
|
47
|
+
outcomeId,
|
|
48
|
+
attemptNumber,
|
|
49
|
+
content: {
|
|
50
|
+
type: 'image',
|
|
51
|
+
imageUrl: 'https://via.placeholder.com/1024x1024/4285F4/FFFFFF?text=Nano+Banana+Generated', // Placeholder
|
|
52
|
+
originalPrompt: imagePrompt,
|
|
53
|
+
revisedPrompt: content,
|
|
54
|
+
dimensions: prompt.dimensions || '1024x1024',
|
|
55
|
+
model: 'gemini-2.5-flash-image',
|
|
56
|
+
style: prompt.style,
|
|
57
|
+
target: prompt.target,
|
|
58
|
+
generatedText: content,
|
|
59
|
+
},
|
|
60
|
+
timestamp: new Date().toISOString(),
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Estimate tokens (Gemini doesn't expose usage in free tier)
|
|
64
|
+
const estimatedTokens = Math.ceil((imagePrompt.length + content.length) / 4);
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
success: true,
|
|
68
|
+
message: 'Nano Banana agent completed successfully',
|
|
69
|
+
tokensUsed: estimatedTokens,
|
|
70
|
+
artifact,
|
|
71
|
+
imageUrl: artifact.content.imageUrl,
|
|
72
|
+
revisedPrompt: content,
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
} catch (error) {
|
|
76
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
77
|
+
return {
|
|
78
|
+
success: false,
|
|
79
|
+
message: `Nano Banana agent execution failed: ${errorMessage}`,
|
|
80
|
+
tokensUsed: 0,
|
|
81
|
+
error: errorMessage,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Creates a mock Nano Banana agent run for testing
|
|
88
|
+
*
|
|
89
|
+
* @param config - Agent configuration
|
|
90
|
+
* @param prompt - Content creation prompt
|
|
91
|
+
* @param outcomeId - Outcome ID
|
|
92
|
+
* @param attemptNumber - Attempt number
|
|
93
|
+
* @returns Mock result matching Nano Banana output format
|
|
94
|
+
*/
|
|
95
|
+
export function mockNanoBananaAgent(
|
|
96
|
+
config: AgentConfig,
|
|
97
|
+
prompt: ContentCreationPrompt,
|
|
98
|
+
outcomeId: string,
|
|
99
|
+
attemptNumber: number
|
|
100
|
+
): ContentCreationResult {
|
|
101
|
+
const mockImageUrl = 'https://via.placeholder.com/1024x1024/4285F4/FFFFFF?text=Nano+Banana+Mock';
|
|
102
|
+
|
|
103
|
+
const artifact: AgentArtifact = {
|
|
104
|
+
agentId: config.id,
|
|
105
|
+
outcomeId,
|
|
106
|
+
attemptNumber,
|
|
107
|
+
content: {
|
|
108
|
+
type: 'image',
|
|
109
|
+
imageUrl: mockImageUrl,
|
|
110
|
+
originalPrompt: prompt.description,
|
|
111
|
+
revisedPrompt: `Nano Banana enhanced: ${prompt.description} with vibrant colors and creative composition`,
|
|
112
|
+
dimensions: prompt.dimensions || '1024x1024',
|
|
113
|
+
model: 'gemini-2.5-flash-image',
|
|
114
|
+
style: prompt.style,
|
|
115
|
+
target: prompt.target,
|
|
116
|
+
generatedText: `Created a stunning image of ${prompt.description} with artistic flair`,
|
|
117
|
+
},
|
|
118
|
+
timestamp: new Date().toISOString(),
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
success: true,
|
|
123
|
+
message: 'Mock Nano Banana agent completed',
|
|
124
|
+
tokensUsed: 350,
|
|
125
|
+
artifact,
|
|
126
|
+
imageUrl: mockImageUrl,
|
|
127
|
+
revisedPrompt: `Nano Banana enhanced: ${prompt.description} with vibrant colors and creative composition`,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI Agents SDK Adapter
|
|
3
|
+
*
|
|
4
|
+
* Adapts OpenAI Agents SDK to work with Earnd's agent configuration system.
|
|
5
|
+
* This allows us to use OpenAI's production-ready agent framework while
|
|
6
|
+
* maintaining compatibility with our outcome-based bounty evaluation.
|
|
7
|
+
*
|
|
8
|
+
* @module agents/adapters/openai
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { Agent, run } from '@openai/agents';
|
|
12
|
+
import type { AgentConfig } from '../agent.schema.js';
|
|
13
|
+
import type { AgentArtifact } from '../../eval/evaluateOutcome.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Lead data structure for SDR agents
|
|
17
|
+
*/
|
|
18
|
+
export interface LeadData {
|
|
19
|
+
email: string;
|
|
20
|
+
company: string;
|
|
21
|
+
companySize: number;
|
|
22
|
+
role: string;
|
|
23
|
+
context?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Result from running an OpenAI agent
|
|
28
|
+
*/
|
|
29
|
+
export interface AgentRunResult {
|
|
30
|
+
success: boolean;
|
|
31
|
+
message: string;
|
|
32
|
+
tokensUsed: number;
|
|
33
|
+
artifact?: AgentArtifact;
|
|
34
|
+
error?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Executes an OpenAI agent configured for lead qualification
|
|
39
|
+
*
|
|
40
|
+
* @param config - Earnd agent configuration
|
|
41
|
+
* @param lead - Lead data to qualify
|
|
42
|
+
* @param outcomeId - ID of the outcome being attempted
|
|
43
|
+
* @param attemptNumber - Current attempt number
|
|
44
|
+
* @returns Result of agent execution with artifact if successful
|
|
45
|
+
*/
|
|
46
|
+
export async function runOpenAIAgent(
|
|
47
|
+
config: AgentConfig,
|
|
48
|
+
lead: LeadData,
|
|
49
|
+
outcomeId: string,
|
|
50
|
+
attemptNumber: number
|
|
51
|
+
): Promise<AgentRunResult> {
|
|
52
|
+
try {
|
|
53
|
+
// Create OpenAI agent with config
|
|
54
|
+
const agent = new Agent({
|
|
55
|
+
name: config.name,
|
|
56
|
+
instructions: config.prompt,
|
|
57
|
+
model: config.modelId,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Construct input for the agent
|
|
61
|
+
const input = `
|
|
62
|
+
You are qualifying a sales lead. Here is the prospect information:
|
|
63
|
+
|
|
64
|
+
Email: ${lead.email}
|
|
65
|
+
Company: ${lead.company} (${lead.companySize} employees)
|
|
66
|
+
Role: ${lead.role}
|
|
67
|
+
${lead.context ? `Additional Context: ${lead.context}` : ''}
|
|
68
|
+
|
|
69
|
+
Your task: ${config.strategyDescription}
|
|
70
|
+
|
|
71
|
+
Write a personalized outreach message that demonstrates buying intent and qualifies this lead.
|
|
72
|
+
Your message should be professional, relevant to their role, and demonstrate understanding of their potential needs.
|
|
73
|
+
`;
|
|
74
|
+
|
|
75
|
+
// Run the agent
|
|
76
|
+
const result = await run(agent, input);
|
|
77
|
+
|
|
78
|
+
// Extract the message from agent output
|
|
79
|
+
const message = result.finalOutput || '';
|
|
80
|
+
|
|
81
|
+
// Create artifact in Earnd format
|
|
82
|
+
const artifact: AgentArtifact = {
|
|
83
|
+
agentId: config.id,
|
|
84
|
+
outcomeId,
|
|
85
|
+
attemptNumber,
|
|
86
|
+
content: {
|
|
87
|
+
message,
|
|
88
|
+
targetEmail: lead.email,
|
|
89
|
+
targetCompany: lead.company,
|
|
90
|
+
targetCompanySize: lead.companySize,
|
|
91
|
+
targetRole: lead.role,
|
|
92
|
+
},
|
|
93
|
+
timestamp: new Date().toISOString(),
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
// Estimate token usage (OpenAI Agents SDK doesn't expose this directly yet)
|
|
97
|
+
// Using approximate: input + output length / 4
|
|
98
|
+
const estimatedTokens = Math.ceil((input.length + message.length) / 4);
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
success: true,
|
|
102
|
+
message: 'Agent completed successfully',
|
|
103
|
+
tokensUsed: estimatedTokens,
|
|
104
|
+
artifact,
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
} catch (error) {
|
|
108
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
109
|
+
return {
|
|
110
|
+
success: false,
|
|
111
|
+
message: `Agent execution failed: ${errorMessage}`,
|
|
112
|
+
tokensUsed: 0,
|
|
113
|
+
error: errorMessage,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Creates a mock agent run for testing without consuming API credits
|
|
120
|
+
*
|
|
121
|
+
* @param config - Agent configuration
|
|
122
|
+
* @param lead - Lead data
|
|
123
|
+
* @param outcomeId - Outcome ID
|
|
124
|
+
* @param attemptNumber - Attempt number
|
|
125
|
+
* @returns Mock result that matches OpenAI agent output format
|
|
126
|
+
*/
|
|
127
|
+
export function mockOpenAIAgent(
|
|
128
|
+
config: AgentConfig,
|
|
129
|
+
lead: LeadData,
|
|
130
|
+
outcomeId: string,
|
|
131
|
+
attemptNumber: number
|
|
132
|
+
): AgentRunResult {
|
|
133
|
+
// Generate a mock message that would pass validation
|
|
134
|
+
const mockMessage = `Hi ${lead.role} at ${lead.company},
|
|
135
|
+
|
|
136
|
+
I noticed your company is doing interesting work in the ${lead.company.toLowerCase().includes('tech') ? 'technology' : 'business'} space.
|
|
137
|
+
|
|
138
|
+
I'd love to schedule a brief call to discuss how our solution could help ${lead.company} streamline operations and drive growth. Would you be open to a 15-minute conversation next week to explore this?
|
|
139
|
+
|
|
140
|
+
Looking forward to connecting about potential next steps.
|
|
141
|
+
|
|
142
|
+
Best regards,
|
|
143
|
+
${config.name}`;
|
|
144
|
+
|
|
145
|
+
const artifact: AgentArtifact = {
|
|
146
|
+
agentId: config.id,
|
|
147
|
+
outcomeId,
|
|
148
|
+
attemptNumber,
|
|
149
|
+
content: {
|
|
150
|
+
message: mockMessage,
|
|
151
|
+
targetEmail: lead.email,
|
|
152
|
+
targetCompany: lead.company,
|
|
153
|
+
targetCompanySize: lead.companySize,
|
|
154
|
+
targetRole: lead.role,
|
|
155
|
+
},
|
|
156
|
+
timestamp: new Date().toISOString(),
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
success: true,
|
|
161
|
+
message: 'Mock agent completed',
|
|
162
|
+
tokensUsed: 500,
|
|
163
|
+
artifact,
|
|
164
|
+
};
|
|
165
|
+
}
|