Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

0.4.0 #40

Merged
merged 16 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading
Loading