Skip to content

Commit

Permalink
0.4.0 (#40)
Browse files Browse the repository at this point in the history
* UI and logging updates

* Add agent force stop route

* Add Groq Llama 3.3 70b

* Improve repo summary index generation

* Add Jira.createIssue

* Default the setTracer checkForcedStoppedFunc for tests

* Add Google Cloud security command center function

* Add Gemini Flash 2.0 support
  • Loading branch information
danielcampagnolitg authored Dec 19, 2024
1 parent a33d39d commit 6848b70
Show file tree
Hide file tree
Showing 31 changed files with 907 additions and 117 deletions.
18 changes: 0 additions & 18 deletions CONVENTIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,3 @@ Use async/await where possible
Test exceptional cases first and return/throw early.

Never edit files name CONVENTIONS.md or .cursorrules

# Test code standards

Unit test files should be in the same directory as the source file.

Any usage of chai-as-promised should use async/await
```
it('should work well with async/await', async () => {
(await Promise.resolve(42)).should.equal(42)
await Promise.reject(new Error()).should.be.rejectedWith(Error);
});
```

# Tool/function classes

Function classes with the @funcClass(__filename) must only have the default constructor.

Always use the Filesystem class in src/functions/storage/filesystem.ts to read/search/write to the local filesystem.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
@if(agentDetails) {
<mat-card *ngIf="agentDetails.state === 'feedback'" class="p-3 mb-4">
<form [formGroup]="feedbackForm" (ngSubmit)="onSubmitFeedback()">
<mat-card-title>Feedback Requested</mat-card-title>
<mat-card-title class="font-bold pl-5 text-lg">Feedback Requested</mat-card-title>
<mat-card-content style="margin-bottom: 0; margin-top: 15px">
<mat-expansion-panel
*ngIf="agentDetails.functionCallHistory && agentDetails.functionCallHistory.length > 0"
Expand Down Expand Up @@ -42,7 +42,7 @@

<mat-card *ngIf="agentDetails?.state === 'error'" class="p-3 mb-4">
<form [formGroup]="errorForm" (ngSubmit)="onResumeError()">
<mat-card-title>Agent Error</mat-card-title>
<mat-card-title class="font-bold pl-5 text-lg">Agent Error</mat-card-title>
<mat-card-content style="margin-bottom: 0; margin-top: 15px">
<mat-expansion-panel *ngIf="agentDetails.error" style="width: 1000px; margin-bottom: 20px">
<mat-expansion-panel-header>
Expand Down Expand Up @@ -75,7 +75,7 @@

<mat-card *ngIf="agentDetails?.state === 'hil'" class="p-3 mb-4">
<form [formGroup]="hilForm" (ngSubmit)="onResumeHil()">
<mat-card-title>Human In Loop check</mat-card-title>
<mat-card-title class="font-bold pl-5 text-lg">Human In Loop check</mat-card-title>
<mat-card-content>
<mat-form-field appearance="fill" class="full-width">
<textarea
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ export class AgentDetailsComponent implements OnInit {
})
).subscribe((response) => {
if (response) {
this.feedbackForm.reset();
this.snackBar.open('Feedback submitted successfully', 'Close', { duration: 3000 });
this.refreshAgentDetails();
}
Expand Down Expand Up @@ -289,9 +290,7 @@ export class AgentDetailsComponent implements OnInit {
});

dialogRef.afterClosed().subscribe((result) => {
if (result) {
this.resumeCompletedAgent(result.resumeInstructions);
}
if (result) this.resumeCompletedAgent(result.resumeInstructions);
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,20 @@ import { MatExpansionModule } from '@angular/material/expansion';
<div class="mb-3 font-medium text-xl">{{ invoked.function_name }}</div>
<div *ngFor="let param of invoked.parameters | keyvalue">
<p><strong>{{ param.key }}:</strong> {{ param.value }}</p>
<div>
<strong>{{ param.key }}:</strong>
<ng-container *ngIf="param.value?.toString().length <= 200">
{{ param.value }}
</ng-container>
<mat-expansion-panel *ngIf="param.value?.toString().length > 200" class="mt-4" #expansionPanel>
<mat-expansion-panel-header [class.expanded-header]="expansionPanel.expanded">
<mat-panel-title class="font-normal" *ngIf="!expansionPanel.expanded">
{{ param.value?.toString().substring(0, 200) }}...
</mat-panel-title>
</mat-expansion-panel-header>
<p>{{ param.value }}</p>
</mat-expansion-panel>
</div>
</div>
<mat-expansion-panel *ngIf="invoked.stdout" class="mt-4">
<mat-expansion-panel-header>
Expand All @@ -29,6 +42,7 @@ import { MatExpansionModule } from '@angular/material/expansion';
</div>
</mat-card>
`,
styles: `.mat-expansion-panel-header.mat-expanded.expanded-header { height: 1.3em; padding-top: 1.2em; }`,
standalone: true,
imports: [CommonModule, MatCardModule, MatExpansionModule],
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ export class NewAgentComponent implements OnInit {
.subscribe({
next: (response) => {
this.snackBar.open('Agent started', 'Close', { duration: 3000 });
this.router.navigate(['/ui/agent', response.data.agentId]).catch(console.error);
this.router.navigate(['/ui/agents', response.data.agentId]).catch(console.error);
},
error: (error) => {
this.snackBar.open(`Error ${error.message}`, 'Close', { duration: 3000 });
Expand Down
19 changes: 19 additions & 0 deletions src/CONVENTIONS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Backend code standards

## Test code standards

Unit test files should be in the same directory as the source file.

Any usage of chai-as-promised should use async/await
```
it('should work well with async/await', async () => {
(await Promise.resolve(42)).should.equal(42)
await Promise.reject(new Error()).should.be.rejectedWith(Error);
});
```

## Tool/function classes

Function classes with the @funcClass(__filename) must only have the default constructor.

Always use the Filesystem class in src/functions/storage/filesystem.ts to read/search/write to the local filesystem.
2 changes: 1 addition & 1 deletion src/agent/cachingCodeGenAgentRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ export async function runCachingCodegenAgent(agent: AgentContext): Promise<Agent
.filter((pkg) => llmPythonCode.includes(`${pkg}.`) || pkg === 'json') // always need json for JsProxyEncoder
.map((pkg) => `import ${pkg}\n`)
.join();
logger.info(`Allowed imports: ${pythonScript}`);

pythonScript += `
from typing import Any, List, Dict, Tuple, Optional, Union
from pyodide.ffi import JsProxy
Expand Down
16 changes: 11 additions & 5 deletions src/agent/codeGenAgentRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@ import { Span, SpanStatusCode } from '@opentelemetry/api';
import { PyodideInterface, loadPyodide } from 'pyodide';
import { runAgentCompleteHandler } from '#agent/agentCompletion';
import { AgentContext } from '#agent/agentContextTypes';
import { AGENT_COMPLETED_NAME, AGENT_REQUEST_FEEDBACK, AGENT_SAVE_MEMORY_CONTENT_PARAM_NAME } from '#agent/agentFunctions';
import {
AGENT_COMPLETED_NAME,
AGENT_COMPLETED_PARAM_NAME,
AGENT_REQUEST_FEEDBACK,
AGENT_SAVE_MEMORY_CONTENT_PARAM_NAME,
REQUEST_FEEDBACK_PARAM_NAME,
} from '#agent/agentFunctions';
import { buildFunctionCallHistoryPrompt, buildMemoryPrompt, buildToolStatePrompt, updateFunctionSchemas } from '#agent/agentPromptUtils';
import { AgentExecution, formatFunctionError, formatFunctionResult } from '#agent/agentRunner';
import { convertJsonToPythonDeclaration, extractPythonCode } from '#agent/codeGenAgentUtils';
Expand Down Expand Up @@ -215,7 +221,7 @@ export async function runCodeGenAgent(agent: AgentContext): Promise<AgentExecuti
.filter((pkg) => llmPythonCode.includes(`${pkg}.`) || pkg === 'json') // always need json for JsProxyEncoder
.map((pkg) => `import ${pkg}\n`)
.join();
logger.info(`Allowed imports: ${pythonScript}`);

pythonScript += `
from typing import Any, List, Dict, Tuple, Optional, Union
from pyodide.ffi import JsProxy
Expand Down Expand Up @@ -265,18 +271,18 @@ main()`.trim();

// Should force completed/requestFeedback to exit the script - throw a particular Error class
if (lastFunctionCall.function_name === AGENT_COMPLETED_NAME) {
logger.info('Task completed');
logger.info(`Task completed: ${lastFunctionCall.parameters[AGENT_COMPLETED_PARAM_NAME]}`);
agent.state = 'completed';
completed = true;
} else if (lastFunctionCall.function_name === AGENT_REQUEST_FEEDBACK) {
logger.info('Feedback requested');
logger.info(`Feedback requested: ${lastFunctionCall.parameters[REQUEST_FEEDBACK_PARAM_NAME]}`);
agent.state = 'feedback';
requestFeedback = true;
} else {
if (!anyFunctionCallErrors && !completed && !requestFeedback) agent.state = 'agent';
}
} catch (e) {
logger.info(`Caught function error ${e.message}`);
logger.info(e, `Caught function error ${e.message}`);
functionErrorCount++;
}
// Function invocations are complete
Expand Down
52 changes: 52 additions & 0 deletions src/agent/forceStopAgent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { agentContext, agentContextStorage } from '#agent/agentContextLocalStorage';
import { isExecuting } from '#agent/agentContextTypes';
import { currentUser } from '#user/userService/userContext';
import { appContext } from '../applicationContext';

const agentsToStop = new Set<string>();

/**
* Terminates the execution of an agent as soon as possible.
* @param agentId
*/
export async function forceStopAgent(agentId: string): Promise<void> {
const agent = await appContext().agentStateService.load(agentId);
if (!agent) throw new Error(`No agent with id ${agentId}`);
if (!isExecuting(agent)) throw new Error(`Agent ${agentId} is not in an executing state`);
if (agent.user.id !== currentUser().id) throw new Error('Cannot stop an agent owned by another user');

agentsToStop.add(agent.agentId);

// Reload the agent every 5 seconds for up to a minute and see if it's not in an executing state
return new Promise((resolve, reject) => {
const startTime = Date.now();
const interval = setInterval(async () => {
const updatedAgent = await appContext().agentStateService.load(agentId);
// Agent should be in an error state if the checkForceStopped() function has been called in its execution
if (!isExecuting(updatedAgent)) {
clearInterval(interval);
agentsToStop.delete(agent.agentId);
resolve();
} else if (Date.now() - startTime >= 60000) {
// 1 minute timeout
clearInterval(interval);
agentsToStop.delete(agent.agentId);
reject(new Error(`Agent ${agentId} did not stop executing within 1 minute`));
}
}, 5000);
});
}

/**
* Checks if the current agent should be force stopped
*/
export function checkForceStopped(): void {
const agent = agentContext();
if (!agent) return;
const agentId = typeof agent === 'string' ? agent : agent.agentId;

if (agentsToStop.has(agentId)) {
agentsToStop.delete(agentId);
throw new Error(`Agent ${agentId} has been force stopped by user ${currentUser().id}`);
}
}
4 changes: 2 additions & 2 deletions src/cache/cacheRetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export function cacheRetry(options: Partial<CacheRetryOptions> = DEFAULTS) {
const cacheRetryOptions = optionsWithDefaults(options);

const cacheService = appContext().functionCacheService;
// console.log(this.constructor.name, methodName, args)

if (cacheRetryOptions.scope) {
const cachedValue = await cacheService.getValue(cacheRetryOptions.scope, this.constructor.name, methodName, args);

Expand All @@ -51,7 +51,7 @@ export function cacheRetry(options: Partial<CacheRetryOptions> = DEFAULTS) {
}

for (let attempt = 1; attempt <= cacheRetryOptions.retries; attempt++) {
logger.debug(`${this.constructor.name}${FUNC_SEP}${methodName} retry ${attempt - 1}`);
if (attempt > 1) logger.debug(`${this.constructor.name}${FUNC_SEP}${methodName} retry ${attempt - 1}`);
try {
let result = originalMethod.apply(this, args);
if (typeof result?.then === 'function') result = await result;
Expand Down
7 changes: 4 additions & 3 deletions src/cli/gaia.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ import { PublicWeb } from '#functions/web/web';
import { LlmCall } from '#llm/llmCallService/llmCall';
import { ClaudeLLMs } from '#llm/services/anthropic';
import { Claude3_5_Sonnet_Vertex, ClaudeVertexLLMs } from '#llm/services/anthropic-vertex';
import { groqLlama3_1_70B } from '#llm/services/groq';
import { groqLlama3_3_70B } from '#llm/services/groq';
import { Gemini_1_5_Flash } from '#llm/services/vertexai';
import { logger } from '#o11y/logger';
import { sleep } from '#utils/async-utils';

import { openAIo1 } from '#llm/services/openai';
import { appContext, initFirestoreApplicationContext } from '../applicationContext';

const SYSTEM_PROMPT = `Finish your answer with the following template: FINAL ANSWER: [YOUR FINAL ANSWER]. YOUR FINAL ANSWER should be a number OR as few words as possible OR a comma separated list of numbers and/or strings. If you are asked for a number, don't use comma to write your number neither use units such as $ or percent sign unless specified otherwise. If you are asked for a string, don't use articles, neither abbreviations (e.g. for cities), and write the digits in plain text unless specified otherwise. If you are asked for a comma separated list, apply the above rules depending of whether the element to be put in the list is a number or a string.`;
Expand Down Expand Up @@ -92,9 +93,9 @@ async function answerGaiaQuestion(task: GaiaQuestion): Promise<GaiaResult> {
// llms: ClaudeVertexLLMs(),
llms: {
easy: Gemini_1_5_Flash(),
medium: groqLlama3_1_70B(),
medium: groqLlama3_3_70B(),
hard: Claude3_5_Sonnet_Vertex(),
xhard: Claude3_5_Sonnet_Vertex(),
xhard: openAIo1(),
},
agentName: `gaia-${task.task_id}`,
type: 'codegen',
Expand Down
6 changes: 3 additions & 3 deletions src/cli/gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import { LlmFunctions } from '#agent/LlmFunctions';
import { agentContext, agentContextStorage, createContext } from '#agent/agentContextLocalStorage';
import { AgentContext, AgentLLMs } from '#agent/agentContextTypes';
import { LLM } from '#llm/llm';
import { ClaudeLLMs } from '#llm/models/anthropic';
import { Claude3_5_Sonnet_Vertex, ClaudeVertexLLMs } from '#llm/models/anthropic-vertex';
import { GPT4oMini } from '#llm/models/openai';
import { ClaudeLLMs } from '#llm/services/anthropic';
import { Claude3_5_Sonnet_Vertex, ClaudeVertexLLMs } from '#llm/services/anthropic-vertex';
import { GPT4oMini, openAIo1 } from '#llm/services/openai';
import { currentUser } from '#user/userService/userContext';
import { initFirestoreApplicationContext } from '../applicationContext';
import { CliOptions, getLastRunAgentId, parseProcessArgs, saveAgentId } from './cli';
Expand Down
1 change: 0 additions & 1 deletion src/cli/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { ClaudeLLMs } from '#llm/services/anthropic';
import { ClaudeVertexLLMs } from '#llm/services/anthropic-vertex';
import { cerebrasLlama3_70b } from '#llm/services/cerebras';
import { deepseekChat } from '#llm/services/deepseek';
import { groqLlama3_1_70B } from '#llm/services/groq';
import { GPT4oMini, openAIo1, openAIo1mini } from '#llm/services/openai';
import { Gemini_1_5_Flash } from '#llm/services/vertexai';
import { codebaseQuery } from '#swe/discovery/codebaseQuery';
Expand Down
1 change: 0 additions & 1 deletion src/cli/swebench.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import { PublicWeb } from '#functions/web/web';
import { LlmCall } from '#llm/llmCallService/llmCall';
import { ClaudeLLMs } from '#llm/services/anthropic';
import { Claude3_5_Sonnet_Vertex, ClaudeVertexLLMs } from '#llm/services/anthropic-vertex';
import { groqLlama3_1_70B } from '#llm/services/groq';
import { Gemini_1_5_Flash } from '#llm/services/vertexai';
import { logger } from '#o11y/logger';
import { SWEBenchAgent, SWEInstance } from '#swe/SWEBenchAgent';
Expand Down
10 changes: 5 additions & 5 deletions src/cli/watch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ export function startWatcher() {
const watcher = fs.watch(watchPath, { recursive: true }, async (event: WatchEventType, filename: string | null) => {
// Early exit if filename is null
if (!filename) return;
console.log(filename);
console.log(event);
console.log(`${event} ${filename}`);

const filePath = path.join(process.cwd(), watchPath, filename);
if (!fileExistsSync(filePath)) {
logger.debug(`${filePath} doesn't exist`);
return;
}
logger.info(`Checking ${filePath}`);
console.log(`Checking ${filePath}`);
try {
const data = await fs.promises.readFile(filePath, 'utf-8');

Expand All @@ -44,7 +44,7 @@ export function startWatcher() {
const lines = data.split('\n');

// Find the index of the first line that starts with '//>>' and ends with '//'
const index = lines.findIndex((line) => line.includes('//>>') && line.trim().endsWith('//'));
const index = lines.findIndex((line) => line.includes('//>') && line.trim().endsWith('//'));

// Early exit if no matching lines are found
if (index === -1) return;
Expand All @@ -54,7 +54,7 @@ export function startWatcher() {
const indentation = line.match(/^\s*/)[0]; // Capture leading whitespace for indentation
const requirements = line.trim().slice(3, -2).trim();

logger.info(requirements);
logger.info(`Extracted requirements: ${requirements}`);

// Formulate the prompt
const prompt = `You are to implement the TODO instructions on the line which starts with //>> and ends with //.\ni.e: ${requirements}`;
Expand Down
5 changes: 3 additions & 2 deletions src/fastify/trace-init/trace-init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { PinoInstrumentation } from './instrumentation';

import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';
import { agentContextStorage } from '#agent/agentContextLocalStorage';
import { checkForceStopped } from '#agent/forceStopAgent';
import { setTracer } from '#o11y/trace';

let initialized = false;
Expand Down Expand Up @@ -104,9 +105,9 @@ function initTrace(): void {
});

const tracer = trace.getTracer(traceServiceName);
setTracer(tracer, agentContextStorage);
setTracer(tracer, agentContextStorage, checkForceStopped);
} else {
setTracer(null, agentContextStorage);
setTracer(null, agentContextStorage, checkForceStopped);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/functionRegistry.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { GoogleCloud } from '#functions/cloud/google-cloud';
import { GoogleCloud } from '#functions/cloud/google/google-cloud';
import { ImageGen } from '#functions/image';
import { Jira } from '#functions/jira';
import { GitHub } from '#functions/scm/github';
Expand Down
File renamed without changes.
Loading

0 comments on commit 6848b70

Please sign in to comment.