Skip to content

Commit

Permalink
feat: simplify the code agent to only to the write utility things
Browse files Browse the repository at this point in the history
  • Loading branch information
aboudzein committed Nov 24, 2024
1 parent 19e94e0 commit 6ba2fcf
Show file tree
Hide file tree
Showing 2 changed files with 12 additions and 173 deletions.
81 changes: 7 additions & 74 deletions packages/server/api/src/app/copilot/tools/code-agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ export function getModel() {

const codeGenerationSchema = z.object({
code: z.string(),
packages: z.array(z.string()).default([]),
inputs: z.array(z.object({
name: z.string(),
type: z.string(),
Expand All @@ -34,7 +33,6 @@ const codeGenerationSchema = z.object({

export async function generateCode(
requirement: string,
sandboxMode: boolean,
): Promise<DeepPartial<typeof codeGenerationSchema>> {
try {
const model = getModel()
Expand All @@ -44,7 +42,7 @@ export async function generateCode(

const systemPrompt = `
You are a TypeScript code generation expert for automation flows.
You are generating code for a SINGLE STEP in an automation flow, NOT a backend service.
You are generating code for a single step in an automation flow, NOT a backend service.
FLOW CONTEXT:
- This code will run as one step in a larger flow
Expand Down Expand Up @@ -83,75 +81,19 @@ export async function generateCode(
- Don't try to handle multiple operations
- Let the flow orchestrate complex processes
Perfect Examples:
1. Simple API Request:
Perfect Example (Gmail API Usage):
{
"code": "export const code = async (inputs: { url: string }) => {\\n try {\\n const response = await fetch(inputs.url);\\n if (!response.ok) throw new Error(\`HTTP error! status: \${response.status}\`);\\n return { data: await response.json() };\\n } catch (error) {\\n throw new Error(\`Request failed: \${error.message}\`);\\n }\\n}",
"packages": [],
"code": "export const code = async (inputs: { accessToken: string }) => {\\n try {\\n const auth = new google.auth.OAuth2();\\n auth.setCredentials({ access_token: inputs.accessToken });\\n\\n const gmail = google.gmail({ version: 'v1', auth });\\n const response = await gmail.users.messages.list({\\n userId: 'me',\\n maxResults: 10\\n });\\n\\n return { messages: response.data.messages || [] };\\n } catch (error) {\\n throw new Error(\`Gmail API error: \${error.message}\`);\\n }\\n}",
"inputs": [
{
"name": "url",
"type": "string",
"description": "API endpoint URL",
"suggestedValue": "https://api.example.com/data"
}
]
}
2. Authenticated API Request:
{
"code": "export const code = async (inputs: { url: string, apiKey: string }) => {\\n try {\\n const response = await fetch(inputs.url, {\\n headers: { Authorization: \`Bearer \${inputs.apiKey}\` }\\n });\\n if (!response.ok) throw new Error(\`HTTP error! status: \${response.status}\`);\\n return { data: await response.json() };\\n } catch (error) {\\n throw new Error(\`Request failed: \${error.message}\`);\\n }\\n}",
"packages": [],
"inputs": [
{
"name": "url",
"type": "string",
"description": "API endpoint URL",
"suggestedValue": "https://api.example.com/data"
},
{
"name": "apiKey",
"name": "accessToken",
"type": "string",
"description": "API key for authentication",
"suggestedValue": "{{ connections.service.apiKey }}"
"description": "Gmail API access token",
"suggestedValue": "{{ connections.gmail.accessToken }}"
}
]
}
3. Data Processing:
{
"code": "export const code = async (inputs: { data: string[] }) => {\\n try {\\n const processed = inputs.data.map(item => item.toUpperCase());\\n return {\\n result: processed,\\n count: processed.length\\n };\\n } catch (error) {\\n throw new Error(\`Processing failed: \${error.message}\`);\\n }\\n}",
"packages": [],
"inputs": [
{
"name": "data",
"type": "string[]",
"description": "Array of strings to process",
"suggestedValue": "{{ ['item1', 'item2'] }}"
}
]
}
BAD EXAMPLES (NEVER DO THESE):
1. ❌ Using external HTTP libraries (Wrong):
import axios from 'axios';
-or-
import { fetch } from 'node-fetch';
✅ Correct:
Use native fetch
2. ❌ OAuth Implementation (Wrong):
const oauth2Client = new google.auth.OAuth2(clientId, clientSecret, redirectUrl);
✅ Correct:
const auth = new google.auth.OAuth2();
auth.setCredentials({ access_token: inputs.accessToken });
3. ❌ Environment Variables (Wrong):
process.env.API_KEY
✅ Correct:
inputs.apiKey
IMPORTANT REMINDERS:
- This is ONE STEP in a flow
- Previous steps provide inputs
Expand All @@ -165,21 +107,13 @@ export async function generateCode(
model,
system: systemPrompt,
schema: codeGenerationSchema,
prompt: `Generate TypeScript code for this automation flow requirement: ${requirement}
Remember:
- ${sandboxMode ? 'External packages are allowed' : 'Use only Node.js native features'}
- Must return useful data for the next step
- Include proper error handling
- All inputs must have suggested values
- Use proper TypeScript types
- Use native fetch for HTTP requests`,
prompt: `Generate TypeScript code for this automation flow requirement: ${requirement}`,
temperature: 0,
})

if (!result?.object) {
return {
code: '',
packages: [],
inputs: [],
}
}
Expand All @@ -190,7 +124,6 @@ Remember:
console.error('Code generation failed:', error)
return {
code: '',
packages: [],
inputs: [],
}
}
Expand Down
104 changes: 5 additions & 99 deletions packages/server/api/src/app/copilot/tools/code-generate.ts
Original file line number Diff line number Diff line change
@@ -1,59 +1,5 @@
import { AskCopilotCodeResponse, AskCopilotRequest, ExecutionMode } from '@activepieces/shared'
import OpenAI from 'openai'
import { AskCopilotCodeResponse, AskCopilotRequest } from '@activepieces/shared'
import { generateCode } from './code-agent'
import { generatePlan } from './plan-agent'
import { system, AppSystemProp, SharedSystemProp } from '@activepieces/server-shared'

function getPerplexityClient() {
const apiKey = system.get(AppSystemProp.PERPLEXITY_API_KEY)
const baseURL = system.get(AppSystemProp.PERPLEXITY_BASE_URL)

if (!apiKey || !baseURL) {
throw new Error('Perplexity API configuration missing')
}

return new OpenAI({
apiKey,
baseURL,
})
}

async function searchWithPerplexity(query: string): Promise<string | null> {
try {
const openai = getPerplexityClient()

const response = await openai.chat.completions.create({
model: 'llama-3.1-sonar-small-128k-online',
messages: [
{
role: 'system',
content: `You are a code research expert. Search and provide detailed information about implementing the requested functionality.
Include:
- Common implementation patterns
- Recommended npm packages
- Best practices
- Code examples
Focus on TypeScript/JavaScript implementations.`,
},
{
role: 'user',
content: query,
},
],
})

return response.choices[0].message.content
}
catch (error) {
console.error('Perplexity search failed:', error)
return null
}
}

interface SearchQuery {
query: string;
reason: string;
}

function createDefaultResponse(error?: string): AskCopilotCodeResponse {
return {
Expand All @@ -75,51 +21,11 @@ export const codeGeneratorTool = {
request: AskCopilotRequest,
): Promise<AskCopilotCodeResponse> {
try {
const sandboxMode = system.getOrThrow(SharedSystemProp.EXECUTION_MODE) !== ExecutionMode.UNSANDBOXED

let plan = await generatePlan(request.prompt, sandboxMode)
if (!plan) {
return createDefaultResponse('Failed to generate plan')
}

if (plan.needsResearch && Array.isArray(plan.searchQueries) && plan.searchQueries.length > 0) {
const searchResults = await Promise.all(
plan.searchQueries
.filter((query): query is SearchQuery =>
query !== null &&
query !== undefined &&
typeof query.query === 'string' &&
typeof query.reason === 'string'
)
.map(query => searchWithPerplexity(query.query))
)

const validResults = searchResults
.filter((result): result is string => result !== null)
.join('\n\n')

plan = await generatePlan(request.prompt, sandboxMode, validResults)
if (!plan) {
return createDefaultResponse('Failed to generate plan after research')
}
}

if (!plan.readyForCode || !plan.context) {
return createDefaultResponse('Plan is not ready for code generation')
}

const result = await generateCode(plan.context, sandboxMode)
const result = await generateCode(request.prompt)
if (!result?.code) {
return createDefaultResponse('Code generation failed')
}

const dependencies: Record<string, string> = {}
result.packages?.forEach((pkg) => {
if (typeof pkg === 'string' && pkg.length > 0) {
dependencies[pkg] = '*'
}
})

const inputs: Record<string, string> = {}
result.inputs?.forEach((input) => {
if (input?.name && input?.suggestedValue) {
Expand All @@ -130,11 +36,11 @@ export const codeGeneratorTool = {
return {
code: result.code,
packageJson: {
dependencies,
dependencies: {},
},
inputs,
icon: plan.suggestedIcon || 'Code2',
title: plan.suggestedTitle || 'Code Implementation',
icon: 'Code2',
title: 'Code Implementation',
}
}
catch (error) {
Expand Down

0 comments on commit 6ba2fcf

Please sign in to comment.